diff options
author | William Bryan <wilbryan@us.ibm.com> | 2015-08-03 12:38:58 -0500 |
---|---|---|
committer | William A. Bryan <wilbryan@us.ibm.com> | 2015-08-03 15:32:27 -0500 |
commit | 420e6d248cc6d2b3c39bc3970e3bb6747b3bddc3 (patch) | |
tree | c9f6691eddba39193e39aa769367e1267fb9fc86 /src/lib/ppc405lib | |
parent | adade8c8ef30ed519322674c762d95663009c5d4 (diff) | |
download | talos-occ-420e6d248cc6d2b3c39bc3970e3bb6747b3bddc3.tar.gz talos-occ-420e6d248cc6d2b3c39bc3970e3bb6747b3bddc3.zip |
new ssx and lib files
Change-Id: I2328b1e86d59e3788910687d762fb70ec680058f
Reviewed-on: http://gfw160.aus.stglabs.ibm.com:8080/gerrit/19503
Reviewed-by: William A. Bryan <wilbryan@us.ibm.com>
Tested-by: William A. Bryan <wilbryan@us.ibm.com>
Diffstat (limited to 'src/lib/ppc405lib')
48 files changed, 9480 insertions, 0 deletions
diff --git a/src/lib/ppc405lib/Makefile b/src/lib/ppc405lib/Makefile new file mode 100644 index 0000000..37ef2ea --- /dev/null +++ b/src/lib/ppc405lib/Makefile @@ -0,0 +1,57 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/lib/ppc405lib/Makefile $ +# +# OpenPOWER OnChipController Project +# +# Contributors Listed Below - COPYRIGHT 2015 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# This Makefile currently builds a single archive, 'libppc405.a', from +# various library source files. +# +# part of the complete application build. +# + +#all generated files from this makefile will end up in obj/$(IMAGE_NAME)/ppc405lib +export SUB_OBJDIR = /ppc405lib + +include img_defs.mk +include libppc405files.mk + +OBJS := $(addprefix $(OBJDIR)/, $(LIBPPC405_OBJECTS)) + +libppc405.a: local + $(AR) crs $(OBJDIR)/libppc405.a $(OBJDIR)/*.o + +.PHONY: clean + +local: $(OBJS) + +$(OBJS) $(OBJS:.o=.d): | $(OBJDIR) + +$(OBJDIR): + mkdir -p $(OBJDIR) + +clean: + rm -fr $(OBJDIR) + +ifneq ($(MAKECMDGOALS),clean) +#include $(OBJS:.o=.d) +endif + diff --git a/src/lib/ppc405lib/README.txt b/src/lib/ppc405lib/README.txt new file mode 100644 index 0000000..cc0076f --- /dev/null +++ b/src/lib/ppc405lib/README.txt @@ -0,0 +1,4 @@ +This directory contains all of the library code that only can run on the ppc405. +For most of the files, the only reason it can not run on the ppe42 is because of +it's dependence on SSX. Eventually, we would like to fix this so that most, if +not all files can be made common. diff --git a/src/lib/ppc405lib/assert.c b/src/lib/ppc405lib/assert.c new file mode 100644 index 0000000..96143a4 --- /dev/null +++ b/src/lib/ppc405lib/assert.c @@ -0,0 +1,71 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/assert.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id: assert.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/assert.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file assert.c +/// \brief Implementation of library routines implied by <assert.h> + +#include "ssx.h" +#include "ssx_io.h" +#include "libssx.h" + +/// The __assert_fail() function is used to implement the assert() interface +/// of ISO POSIX (2003). The __assert_fail() function prints the given \a +/// file filename, \a line line number, \a function function name and a +/// message on the standard error stream then causes a kernel panic. If there +/// is no standard error stream then the error message is printed on the \a +/// ssxout (printk()) stream. +/// +/// If function is NULL, __assert_fail() omits information about the +/// function. The aguments \a assertion, \a file, and \a line must be +/// non-NULL. + +void +__assert_fail(const char *assertion, + const char *file, + unsigned line, + const char *function) +{ + FILE *stream; + + stream = stderr; + if (stream == 0) { + stream = ssxout; + } + + fprintf(stream, "%s:%u:%s%s Assertion '%s' failed\n", + file, line, + function ? function : "", function ? ":" : "", + assertion); + + SSX_PANIC(ASSERTION_FAILURE); +} + diff --git a/src/lib/ppc405lib/byte_pool.c b/src/lib/ppc405lib/byte_pool.c new file mode 100644 index 0000000..d382abc --- /dev/null +++ b/src/lib/ppc405lib/byte_pool.c @@ -0,0 +1,1442 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/byte_pool.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id$ + +/// \file byte_pool.c +/// \brief An implementation of a constant-time malloc() algorithm. +/// +/// The 'byte-pool' API defined in this file is similar to ThreadX' byte-pool +/// operations, with the major difference that here there is no concept of a +/// thread blocking on memory allocation. This is a concept that is difficult +/// to implement correctly and efficiently, and several semantic options +/// exist for how allocation and freeing memory should work in the presence of +/// blocked threads. The application is always free to implement a blocking +/// API using these APIs and SSX synchronization primitives. +/// +/// For convenience this implementation also provides malloc() and calloc() +/// calls. These APIs depend on the application creating a byte pool and +/// assigning it to the library variable \a _malloc_byte_pool. +/// +/// \todo Consider separating the idea of creating a byte pool with a specific +/// maximum-sized block, from the idea of adding memory to a byte pool. The +/// idea is to allow pieces of memory required during initialization of the +/// application to be added back to the pool at run time. We could also +/// simply add an API to allow memory to be added to a previously created +/// pool, with the special case that if the block were 'too big' that it would +/// be split into smaller chunks. +/// +/// \todo Consider +/// adding an option to store a pointer to the originating byte pool in the +/// block header. This would allow deallocation of any block with free() +/// regardless of which pool it was allocated from, at a cost of 1 pointer per +/// block. This would simplify some of our validation test cases. We could +/// also accomplish this by having each pool register ranges of addresses that +/// it allocates from, but that would require a search every time we freed a +/// block. + +#include "ssx.h" +#include "byte_pool.h" + +// This is an implementation of the TLSF (Two-Level Segregate Fit) algorithm +// described by M. Masmano, I. Ripoll and A Crespol, +// http:/rtportal.upv.es/rtmalloc. A couple of their papers and presentations +// are archived in /lib/doc/tlsf. This is a 'clean-room' implementation of +// their published ideas based solely on their papers and presentations. No +// part of their GPL implementation was used to create the byte-pool facility +// implemented here. The algorithm as implemented here should port without +// problem to either 32-bit or 64-bit implementations. +// +// TLSF has the nice property that it is a constant-time algorithm, both for +// allocation and freeing allocated blocks. This property is guaranteed by +// always trading space for speed in the algorithm. This means that we can +// (and do) run the allocation and freeing in a critical section. With all +// error checking and statistics, a worst-case byte_bool_alloc() was +// timed at 318 PowerPC instructions in the PgP OCC (PPC405) Simics +// simulation. A worst-case byte_pool_free() was timed at 368 PowerPC +// instructions. These times are expected to equate to ~3us in OCC, a +// reasonable value for a critical section. [The above times include all +// error checking]. +// +// An allocation unit requires two pointers of overhead - a pointer to the +// previous block and a pointer to the next block (both in terms of linear +// addresses). The minimum block size also includes the requirement for two +// extra pointers used to link free blocks into their free lists. The final +// size of the block (including user data) is simply computed as (next - self) +// in terms of linear addresses. +// +// An allocated block is marked by setting the low-order bit of the 'previous' +// pointer, which bit is otherwise guaranteed to be 0 due to alignment +// restrictions. Whenever a block is freed it is immediately merged with the +// previous and next blocks, if possible. Several places in the block +// splitting and merging code take advantage of this invariant, and assume +// that if a block is not already merged with its 'next' partner, then the +// 'next' partner must be allocated. Sentinel blocks are allocated at either +// end of the managed area to avoid special checks for the first and last +// memory blocks during merging. +// +// The 'Two-Level' in TLSF refers to the fact that there are multiple free +// lists arranged in a 2-dimensional array. Each free lists contains blocks +// that fall into a particular size range. The free list pointers and other +// data structures described below are carved out of the initial free area +// when a byte pool is initialized. +// +// The first dimension of the free list array is simply the floor(log2(size)) +// of the block. For the second dimension, a tuning parameter selects how many +// columns each row in the table will contain. The number of columns must be +// an even power-of-2. Each column represents a fixed power-of-2 size +// increment of block sizes. Given a block size, it is easy to compute the +// row and column indices of a free list containing blocks of that size with +// shifting and masking operation. +// +// It is assumed that the C type 'unsigned long' is the same size as a +// pointer. Therefore the number of rows in the table is less than or equal +// to the number of bits in an unsigned long. The number of columns is also +// restricted to being in the range (1, 2, 4, ... number of bits in unsigned +// long). +// +// The above restrictions make it very fast (and constant time) to find a free +// list that contains a block that will satisfy the allocation request. The +// byte pool structure maintains a 'row status' word that indicates whether +// there are any blocks free in any of the free lists of the row. Each row +// also has an associated 'column status' word that indicates which free lists +// have blocks free. A row status bit is set if and only if at least one bit +// in the column status for that row is set. +// +// Note that although the 32-bit PowerPC implementation conceptually contains +// a 32x32 array of free list pointers, only the free list pointers actually +// required to hold the representable blocks are allocated. +// +// The algorithm uses the GCC __builtin_clzl() function to count leading zeros +// in the status words to find rows/columns that contain free blocks. This +// generates the 'cntlzw' instruction on 32-bit PowerPC and a similar +// instruction on X86. So the algorithm is also portable across machines - +// which simplifies testing. +// +// A couple of final facts: When the application requests memory, the block +// header overhead is added to the request and we look for a free list +// guaranteed to contain blocks of the requested size. That means that the +// request size must be rounded up to the next free list size, to avoid having +// to search a list that might not contain a block of the proper size. This +// leads to cases where allocation will fail, even though the requested memory +// is actually available. That's just the price we have to pay for a +// constant-time guarantee. +// +// This memory allocator will never be used in mission-mode for hard-real-time +// applications, so the statistics are always kept up-to-date. This adds some +// overhead, but does not effect the constant-time behavior. +// +// Given the above description, hopefuly the brief comments with the +// implementation will make sense. + + +/// The byte pool for use by malloc() and calloc(). +/// +/// The application must define a byte pool and assign it to _malloc_byte_pool +/// in order for malloc() and calloc() to work. + +BytePool *_malloc_byte_pool = 0; + + +// The byte pool memory block header. +// +// Each memory block requires 2 pointers of overhead - the pointers to the +// previous and next (in terms of linear addresses) blocks. The low-order bit +// of the \a previous pointer is used as the \e allocated flag, and is set +// when a block is allocated. The size of a block is computed simply as the +// \a next - \a self. This header layout makes it very simple to merge blocks +// when they are deallocated. + +typedef struct ByteBlock { + + // Pointer to the previous (in terms of linear address) block. + // + // The low-order bit of the pointer is set to indicate a block that has + // been allocated. + struct ByteBlock *previous; + + // Pointer to the next (in terms of linear address) block. + // + // The size of the block is computed simply as \a next - \a self. + struct ByteBlock *next; + +} ByteBlock; + + +// A free byte-pool memory block +// +// Blocks stored in free lists require an additional 2 pointers of +// overhead. The blocks are doubly-linked in the free lists to make deletion +// a constant-time operation. Note that the previous pointer is a pointer to +// a pointer to a ByteBlock - it may be pointing to the free list +// header. Since all blocks must be freeable, this structure defines the +// minimum block size. + +typedef struct FreeByteBlock { + + // The base object + ByteBlock block; + + // Pointer to the next block in the free list + struct FreeByteBlock *next; + + // Pointer to the \a next pointer of the previous element in the free + // list, or a pointer to the free list header. + struct FreeByteBlock **previous; + +} FreeByteBlock; + + +// All blocks will be aligned to this size, so this size also defines the +// minimum quantum of memory allocation. The coice of 8 should give +// good results for both 32-bit and 64-bit implementations. +// +// NB : This implmentation assumes that the ByteBlock and FreeByteBLock are +// aligned to this alignment - if this constant is ever changed from 8 then +// the ByteBlock and FreeByteBlock may need to be padded to meet the alignment +// assumptions, and the \a minimum_block_size may need to be adjusted. + +#define ALIGNMENT 8 + + +// An unsigned long, big-endian bit mask + +#define UL_BE_MASK(i) \ + ((unsigned long)1 << (BITS_PER_UNSIGNED_LONG - (i) - 1)) + + +// Align a value to the alignment. The direction is either positive or +// negative to indicate alignment up or down. + +static inline unsigned long +align(unsigned long x, int direction) +{ + if (x % ALIGNMENT) { + if (direction > 0) { + return x + (ALIGNMENT - (x % ALIGNMENT)); + } else { + return x - (x % ALIGNMENT); + } + } else { + return x; + } +} + + +// Compute the floor(log2(x)) of x. This is used to compute the row indices +// of blocks based on the block size. + +static inline int +floor_log2(unsigned long x) +{ + return BITS_PER_UNSIGNED_LONG - 1 - __builtin_clzl(x); +} + + +// In theory the tuning parameters might vary based on the amount of memory +// being managed, but for now we simply use constants. +// +// The minimum block size includes both the size of the header, as well as the +// requirement that the number of columns be <= the mimumum block size to make +// the addressing uniform. For example, on PPC405 the minimum block size is 16 +// bytes (4 pointers) -- unless the number of columns is 32, in which case it +// has to grow to 32 bytes. +// +// Note that no matter what, we may allocate free list pointers in the +// lower-numbered rows that will never be populated due to alignment +// constraints. + +#ifndef BYTE_POOL_TLSF_COLUMNS +#define BYTE_POOL_TLSF_COLUMNS 8 /* 1,2,4, ... BITS_PER_UNSIGNED_LONG */ +#endif + +static void +compute_tuning(BytePool *pool, size_t size, int columns) +{ + int log2_min_size; + + pool->columns = columns; + pool->log2_columns = floor_log2(pool->columns); + pool->column_mask = (1 << pool->log2_columns) - 1; + + log2_min_size = MAX(pool->log2_columns, floor_log2(sizeof(FreeByteBlock))); + pool->minimum_block_size = align(1 << log2_min_size, 1); +} + + +// Compute the size of a block + +static inline size_t +block_size(ByteBlock *block) +{ + return (unsigned long)(block->next) - (unsigned long)block; +} + + +/// Return implementation information for a block +/// +/// \param memory The memory block to query. This pointer must have been +/// returned by one of the byte_pool functions or derivitives, or may also be +/// 0. +/// +/// \param actual_address : Returned as the address of the block header. +/// +/// \param actual_size Returned as the size of the complete block, including +/// the header. +/// +/// \param useful_size : Returned as the actual amount of space available from +/// \a memory to the end of the block. The \a useful_size may be useful to +/// applications that allocate big blocks then carve them up into smaller +/// structures. +/// +/// Note that any of \a actual_address, \a actual_size and \a useful_size may +/// be passed in as 0 if the caller does not require the information. + +void +byte_pool_block_info(void* memory, + void** actual_address, size_t* actual_size, + size_t* useful_size) +{ + ByteBlock* block; + + if (memory == 0) { + + if (actual_address) *actual_address = 0; + if (actual_size) *actual_size = 0; + if (useful_size) *useful_size = 0; + + } else { + + // This implementation uses the convention that if the \a next pointer + // of the putative ByteBlock == 1, then this is actually an aligned + // allocation and the actual ByteBlock is located at the address + // contained in the \a previous field of the dummy header. + + block = (ByteBlock *)(((unsigned long)memory) - sizeof(ByteBlock)); + if ((int)(block->next) == 1) { + block = block->previous; + } + + if (actual_address) *actual_address = block; + if (actual_size) *actual_size = block_size(block); + if (useful_size) + *useful_size = + (unsigned long)(block->next) - (unsigned long)memory; + } +} + + +// Mark a block as allocated by setting the low-order bit of the \a previous +// pointer. + +static inline ByteBlock * +allocated(ByteBlock *p) +{ + return (ByteBlock *)((unsigned long)p | 1ul); +} + + +static void +mark_allocated(BytePool *pool, ByteBlock *block) +{ + size_t bytes = block_size(block); + + pool->bytes_allocated += bytes; + pool->bytes_free -= bytes; + pool->blocks_allocated += 1; + pool->blocks_free -= 1; + + block->previous = allocated(block->previous); +} + + +// Mark a block as free by clearing the low-order bit of the \a previous +// pointer. + +static inline ByteBlock * +deallocated(ByteBlock *p) +{ + return (ByteBlock *)((unsigned long)p & ~1ul); +} + + +static void +mark_free(BytePool *pool, ByteBlock *block) +{ + size_t bytes = block_size(block); + + pool->bytes_allocated -= bytes; + pool->bytes_free += bytes; + pool->blocks_allocated -= 1; + pool->blocks_free += 1; + + block->previous = deallocated(block->previous); +} + + +// Check for a block being free + +static inline int +block_is_free(ByteBlock *block) +{ + return (((unsigned long)(block->previous)) & 1ul) == 0; +} + + +// Normalize a 'previous' pointer + +static inline ByteBlock * +normalize_previous(ByteBlock *previous) +{ + return (ByteBlock *)((unsigned long)previous & ~1ul); +} + + +// Check for correct linkage. This is such a critical check for application +// memory corruption that it is always done. + +static int +check_linkage(ByteBlock *block) +{ + if (normalize_previous(block->next->previous) != block) { + printk("byte_pool: Forward linkage error\n" + " block : %p\n" + " block->next : %p\n" + " block->next->previous : %p\n", + block, + block->next, + block->next->previous); + SSX_ERROR(BYTE_POOL_REVERSE_LINKAGE); + } else if (normalize_previous(block->previous)->next != block) { + printk("byte_pool: linkage error\n" + " block->previous : %p\n" + " block->pevious->next : %p\n" + " block : %p\n", + block->previous, + block->previous->next, + block); + SSX_ERROR(BYTE_POOL_FORWARD_LINKAGE); + } + return 0; +} + + +// Mark a free list as empty + +static inline void +mark_empty(BytePool *pool, int row, int column) +{ + pool->column_status[row] &= ~UL_BE_MASK(column); + if (pool->column_status[row] == 0) { + pool->row_status &= ~UL_BE_MASK(row); + } +} + + +// Mark a free list as non-empty + +static inline void +mark_non_empty(BytePool *pool, int row, int column) +{ + pool->column_status[row] |= UL_BE_MASK(column); + pool->row_status |= UL_BE_MASK(row); +} + + +// Convert a size into row and column indices + +static inline void +size2rc(BytePool *pool, size_t size, int *row, int *column) +{ + *row = floor_log2(size); + *column = (size >> (*row - pool->log2_columns)) & pool->column_mask; +} + + +// Given a block size, find the free list that contains blocks of that size +// (or greater, up to the next free list). When called during block freeing, +// the block size is known to be valid. When called during allocation, the +// block size may be invalid (too big), in which case 0 is returned. + +static FreeByteBlock ** +find_free_list(BytePool *pool, size_t size, int *row, int *column) +{ + size2rc(pool, size, row, column); + if (*row > pool->last_row) { + return 0; + } + return &((pool->free[*row])[*column]); +} + + +// Remove an arbitrary block from its free list due to a merging operation. + +static void +unlink_free_block(BytePool *pool, ByteBlock *block) +{ + FreeByteBlock **free_list; + FreeByteBlock *free_block; + int row, column; + + free_list = find_free_list(pool, block_size(block), &row, &column); + + if (SSX_ERROR_CHECK_KERNEL) { + if (free_list == 0) { + SSX_PANIC(BYTE_POOL_NULL_FREE_LIST); + } + } + + // Unlink the block from the free list + + free_block = (FreeByteBlock *)block; + *(free_block->previous) = free_block->next; + if (free_block->next) { + free_block->next->previous = free_block->previous; + } + + // If the free list is now 0, mark the free list as empty. + + if (*free_list == 0) { + mark_empty(pool, row, column); + } +} + + +// Link a block into the head of its free list due to freeing memory + +static void +link_free_block(BytePool *pool, ByteBlock *block) +{ + FreeByteBlock **free_list; + FreeByteBlock *free_block; + int row, column; + + free_list = find_free_list(pool, block_size(block), &row, &column); + + if (SSX_ERROR_CHECK_KERNEL) { + if (free_list == 0) { + SSX_PANIC(BYTE_POOL_NULL_FREE_LIST); + } + } + + // Link the block into the free list, and mark the free list as + // non-empty. + + free_block = (FreeByteBlock *)block; + + free_block->next = *free_list; + if (*free_list) { + (*free_list)->previous = &(free_block->next); + } + *free_list = free_block; + free_block->previous = free_list; + + mark_non_empty(pool, row, column); +} + + +// Round up the block size (if required) to the next column. Note that the +// block_size input here is aligned, and remains aligned even after rounding. + +static size_t +round_up_size(BytePool *pool, size_t block_size) +{ + size_t residue, column_span, column_mask; + int row = floor_log2(block_size); + + column_span = 1 << (row - pool->log2_columns); + column_mask = column_span - 1; + residue = block_size & column_mask; + + if (residue == 0) { + return block_size; + } else { + return block_size + (column_span - residue); + } +} + + +// The implemenation of freeing a block of memory. When freed, a block is +// immediately merged with its neighbors if possible, and the final merged +// block is inserted into the proper free list. +// +// The linkage check is done here so that it can also protect internal uses of +// this API (but only if SSX errors lead to panics, the expected default). + +static int +byte_pool_free_block(BytePool *pool, ByteBlock *block) +{ + int rc; + SsxMachineContext ctx; + + rc = check_linkage(block); + if (rc) return rc; + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + pool->free_calls++; + + mark_free(pool, block); + + if (block_is_free(block->next)) { + + // Merge next block into current block + + unlink_free_block(pool, block->next); + + block->next = (ByteBlock *)((unsigned long)(block->next) + + block_size(block->next)); + block->next->previous = allocated(block); + + pool->blocks_free--; + } + + if (block_is_free(block->previous)) { + + // Merge current block into previous block + + unlink_free_block(pool, block->previous); + + block->previous->next = + (ByteBlock *)((unsigned long)(block->previous->next) + + block_size(block)); + block = block->previous; + block->next->previous = allocated(block); + + pool->blocks_free--; + } + + // Finally, insert the block into the proper free list. + + link_free_block(pool, block); + + ssx_critical_section_exit(&ctx); + + return 0; +} + + +/// Free a block of memory back to a byte pool +/// +/// \param pool A pointer to the BytePool structure that allocated the memory. +/// +/// \param memory A pointer to memory returned by byte_pool_alloc() or +/// byte_pool_alloc_aligned() for the pool. This pointer may be NULL (0), in +/// which case the byte_pool_free() request succeeds immediately. +/// +/// The part of this API that manipulates the \a pool runs as an +/// SSX_NONCRITICAL critical section. byte_pool_free() uses a constant-time +/// algorithm. +/// +/// Return values other then SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval 0 Success +/// +/// \retval -BYTE_POOL_INVALID_OBJECT The \a pool argument was NULL (0). +/// +/// \retval -BYTE_POOL_INVALID_ARGUMENT The block is not marked as being +/// allocated, or does not appear to have been allocated from this byte_pool. +/// +/// \retval -BYTE_POOL_LINKAGE_ERROR The block being freed is not linked +/// correctly with the other blocks managed by the pool, most likely +/// indicating that the memory being freed was not allocated by +/// byte_pool_alloc(), or that memory corruption has occured. + +// This implementation uses the convention that if the \a next pointer of the +// putative ByteBlock == 1, then this is actually an aligned allocation and +// the actual ByteBlock is located at the address contained in the \a previous +// field of the dummy header. + +int +byte_pool_free(BytePool *pool, void *memory) +{ + ByteBlock *block; + + if (memory == 0) { + return 0; + } + + block = (ByteBlock *)(((unsigned long)memory) - sizeof(ByteBlock)); + if ((int)(block->next) == 1) { + if (0) { + printk("byte_pool_free(%p, %p) [%p] : Aligned\n", + pool, memory, block); + } + block = block->previous; + } + + if (0) { + printk("byte_pool_free(%p, %p) [%p] : %d %d %d\n", + pool, memory, block, + block_is_free(block), + block < pool->first_block, + block > pool->last_block); + } + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(pool == 0, BYTE_POOL_INVALID_OBJECT); + SSX_ERROR_IF(block_is_free(block) || + (block < pool->first_block) || + (block > pool->last_block), + BYTE_POOL_INVALID_ARGUMENT); + } + + return byte_pool_free_block(pool, block); +} + + +/// Create a BytePool with explicit specification of tuning parameters +/// +/// This routine is the real body of byte_pool_create(), however this +/// underlying interface is provided for testing and experimentation and allows +/// the specification of non-default tuning parameters. +/// +/// There is actually only one tuning parameter for TLSF - the number of +/// columns. The number of columns must be an even power of two no larger +/// than the number of bits in an unsigned long. +/// +/// Return values other then SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval -BYTE_POOL_INVALID_OBJECT The \a pool pointer was NULL (0). +/// +/// \retval -BYTE_POOL_INVALID_ARGUMENT Either the \a memory pointer was NULL +/// (0), the amount of memory was insufficient for the management overhead, or +/// the parameterization was invalid. + +int +byte_pool_create_tuned(BytePool *pool, void *memory, size_t size, + int columns) +{ + size_t overhead, free_list_overhead; + unsigned long memory_ul, aligned_memory; + int i; + FreeByteBlock **free; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(pool == 0, BYTE_POOL_INVALID_OBJECT); + SSX_ERROR_IF((memory == 0) || + (columns < 1) || + ((columns & (columns - 1)) != 0) || + (floor_log2(columns) > floor_log2(BITS_PER_UNSIGNED_LONG)), + BYTE_POOL_INVALID_ARGUMENT); + } + + // Compute tuning parameters + + compute_tuning(pool, size, columns); + + // Clear free list vector pointers and column status + + for (i = 0; i < BITS_PER_UNSIGNED_LONG; i++) { + pool->free[i] = 0; + pool->column_status[i] = 0; + } + + // Determine the first and last allocated rows. + + pool->first_row = floor_log2(pool->minimum_block_size); + pool->last_row = floor_log2(size); + + // The dynamic overhead consists of aligment overhead, 2 sentinel nodes, + // the vectors of pointers to free lists, plus 2 alignments. There must + // also be enough room for at least 1 block to allocate. + + memory_ul = (unsigned long)memory; + aligned_memory = align(memory_ul, 1); + + free_list_overhead = + (((pool->last_row - pool->first_row + 1) * pool->columns) * + sizeof(FreeByteBlock *)); + + overhead = + (aligned_memory - memory_ul) + + (2 * sizeof(ByteBlock)) + + free_list_overhead + + (2 * ALIGNMENT) + + pool->minimum_block_size; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(overhead >= size, BYTE_POOL_INVALID_ARGUMENT); + } + + // Allocate the overhead items. The free list vectors and column status + // arrays are carved out and zeroed. For good measure we re-align after + // each of these operations. The sentinel blocks are carved off of either + // end of the remaining free space and marked allocated. The remaining + // initial "big block" is also initialized (as if it were allocated). + + size = size - (aligned_memory - memory_ul); + size = align(size, -1); + + pool->row_status = 0; + + free = (FreeByteBlock **)aligned_memory; + memset((void *)free, 0, free_list_overhead); + + aligned_memory += free_list_overhead; + size -= free_list_overhead; + aligned_memory = align(aligned_memory, 1); + size = align(size, -1); + + for (i = pool->first_row; i <= pool->last_row; i++) { + pool->free[i] = free; + free += pool->columns; + } + + pool->first_block = (ByteBlock *)aligned_memory; + aligned_memory += sizeof(ByteBlock); + size -= sizeof(ByteBlock); + + pool->big_block = (ByteBlock *)aligned_memory; + + pool->last_block = + (ByteBlock *)(aligned_memory + size - sizeof(ByteBlock)); + size -= sizeof(ByteBlock); + + pool->first_block->next = pool->big_block; + pool->first_block->previous = 0; + mark_allocated(pool, pool->first_block); + + pool->last_block->next = 0; + pool->last_block->previous = pool->big_block; + mark_allocated(pool, pool->last_block); + + pool->big_block->previous = pool->first_block; + pool->big_block->next = pool->last_block; + + // Initialize statistics + + pool->bytes_allocated = 0; + pool->bytes_free = block_size(pool->big_block); + pool->initial_allocation = pool->bytes_free; + pool->blocks_allocated = 0; + pool->blocks_free = 1; + pool->alloc_calls = 0; + pool->free_calls = 0; + + // Free the big block and we're ready to go. + + mark_allocated(pool, pool->big_block); + byte_pool_free_block(pool, pool->big_block); + + return 0; +} + + +/// Create a BytePool +/// +/// \param pool A pointer to an uninitialized BytePool structure +/// +/// \param memory A pointer to the memory to be managed by the BytePool +/// +/// \param size The size of the managed area in bytes +/// +/// byte_pool_create() sets up the \a memory area to be used as a memory pool +/// for malloc()-style allocation using byte_pool_alloc() and +/// byte_pool_free(). Note that the actual memory area available for +/// allocation will be smaller than \a size due to alignment, and reservation +/// of a portion of the area for management overhead. +/// +/// Return values other then SSX_OK (0) are errors; see \ref ssx_errors +/// +/// \retval -BYTE_POOL_INVALID_OBJECT The \a pool pointer was NULL (0). +/// +/// \retval -BYTE_POOL_INVALID_ARGUMENT Either the \a memory pointer was NULL +/// (0), the amount of memory was insufficient for the management overhead, or +/// the parameterization was invalid. + +int +byte_pool_create(BytePool *pool, void *memory, size_t size) +{ + return byte_pool_create_tuned(pool, memory, size, BYTE_POOL_TLSF_COLUMNS); +} + + +/// Allocate memory from a byte pool +/// +/// \param pool A pointer to an initialized BytePool +/// +/// \param memory An address to recieve a pointer to the allocated memory. +/// This address will be set to NULL (0) if the allocation request can not be +/// satisfied (or the \a size is 0). +/// +/// \param size The number of bytes to allocate. +/// +/// The part of this API that manipulates the \a pool runs as an +/// SSX_NONCRITICAL critical section. byte_pool_alloc() uses a constant-time +/// algorithm. +/// +/// Return values other than 0 are not necessarily errors; see \ref +/// ssx_errors. +/// +/// The following return codes are not considered errors: +/// +/// \retval 0 Success +/// +/// \retval -BYTE_POOL_NO_MEMORY The allocation request could not be +/// satisfied. The memory pointer will also be NULL (0) in this case. +/// +/// The following return codes are considered errors: +/// +/// \retval -BYTE_POOL_INVALID_OBJECT The \a pool argument was NULL (0). +/// +/// \retval -BYTE_POOL_INVALID_ARGUMENT The \a memory argument is NULL (0). + +int +byte_pool_alloc(BytePool *pool, void **memory, size_t size) +{ + SsxMachineContext ctx; + size_t request_size, actual_size; + int found, row, column; + unsigned long row_status, column_status; + FreeByteBlock **free_list; + FreeByteBlock *free_block; + ByteBlock *block; + ByteBlock *residue_block; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(pool == 0, BYTE_POOL_INVALID_OBJECT); + SSX_ERROR_IF(memory == 0, BYTE_POOL_INVALID_ARGUMENT); + } + + // Quickly dispense with NULL requests + + if (size == 0) { + *memory = 0; + return 0; + } + + // Compute the requested block size (which includes the header). If the + // size went down we overflowed due to a huge request (which can't be + // filled). Otherwise if the request is small it is boosted up to the + // (aligned) minimum size. To guarantee fast search, the requested size + // must then be rounded up to a size that is represented in the 2-D array + // of free list pointers. + + request_size = align(size + sizeof(ByteBlock), 1); + if (request_size < size) { + *memory = 0; + return -BYTE_POOL_NO_MEMORY; + } + + if (request_size < pool->minimum_block_size) { + request_size = pool->minimum_block_size; + } + + request_size = round_up_size(pool, request_size); + + // Up to this point, all accesses of the memory pool object have been to + // read only constants. Now we get serious. + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + pool->alloc_calls++; + + // See if a block of the correct or larger size exists in the row. The + // search is first via a single bit in the row_status. If that hits then + // we check for columns >= the target column. + + found = 0; + size2rc(pool, request_size, &row, &column); + + if (pool->row_status & UL_BE_MASK(row)) { + + column_status = pool->column_status[row] & + ((UL_BE_MASK(column) << 1) - 1); + + if (column_status != 0) { + column = __builtin_clzl(column_status); + found = 1; + } + } + + // If the block was not found in the 'optimum' row, look in all rows of + // larger size and take the first block that fits. + + if (!found) { + + row_status = pool->row_status & (UL_BE_MASK(row) - 1); + + if (row_status != 0) { + row = __builtin_clzl(row_status); + column = __builtin_clzl(pool->column_status[row]); + found = 1; + } + } + + // Another out of memory case. + + if (!found) { + ssx_critical_section_exit(&ctx); + *memory = 0; + return -BYTE_POOL_NO_MEMORY; + } + + // Now we can get the pointer to the free list and take the block. + + free_list = &((pool->free[row])[column]); + + if (SSX_ERROR_CHECK_KERNEL) { + if ((free_list == 0) || (*free_list == 0)) { + SSX_PANIC(BYTE_POOL_INVALID_FREE_LIST); + } + } + + free_block = *free_list; + *free_list = free_block->next; + if (free_block->next) { + free_block->next->previous = free_list; + } else { + mark_empty(pool, row, column); + } + + // Mark the block as allocated + + block = (ByteBlock *)free_block; + mark_allocated(pool, block); + + // If there is enough residue, split the excess memory off of the end of + // the block. This is a kind of dummy transaction for our statistical + // purposes. + + actual_size = block_size(block); + if ((actual_size - request_size) >= pool->minimum_block_size) { + + residue_block = (ByteBlock *)((unsigned long)block + request_size); + + residue_block->next = block->next; + residue_block->previous = block; + residue_block->previous->next = residue_block; + residue_block->next->previous = allocated(residue_block); + + pool->blocks_allocated++; + byte_pool_free_block(pool, residue_block); + pool->free_calls--; + } + + // Set the memory pointer to the area to be used by the application and + // return. + + *memory = (void *)((unsigned long)block + sizeof(ByteBlock)); + + ssx_critical_section_exit(&ctx); + + if (0) { + ByteBlock* block = + (ByteBlock*)((unsigned long)*memory - sizeof(ByteBlock)); + + printk("byte_pool_alloc(%p, -> %p, %zu)\n" + " request_size = %u, Previous = %p, Next = %p\n", + pool, *memory, size, + request_size, block->previous, block->next); + } + + return 0; +} + + +/// Allocate memory from a byte pool and clear +/// +/// byte_pool_calloc() allocates memory using byte_pool_alloc() then clears +/// the memory area using memset(). The arguments conform to the POSIX +/// standard for calloc(). See byte_pool_alloc() for return codes and usage +/// notes. + +int +byte_pool_calloc(BytePool *pool, void **memory, size_t nmemb, size_t size) +{ + int rc; + + rc = byte_pool_alloc(pool, memory, nmemb * size); + if (rc || (*memory == 0)) { + return rc; + } + + memset(*memory, 0, nmemb * size); + + return 0; +} + + +/// Allocate an aligned memory area +/// +/// \param pool A pointer to an initialized BytePool +/// +/// \param memory An address to recieve a pointer to the allocated memory. +/// This address will be set to NULL (0) if the allocation request can not be +/// satisfied (or the \a size is 0). +/// +/// \param size The size of the memory area required (in bytes). This can be +/// any size - it \e does \e not have to be a multiple of the aligned size (as +/// is required by other common aligned memory allocators). +/// +/// \param alignment The alignment constraint, specified as the base 2 +/// logarithm of the alignment. For example, to align on a 128-byte boundary +/// the \a alignment would be specified as 7. +/// +/// byte_pool_alloc_aligned() is a convenience interface for allocating memory +/// with a guaranteed alignment. The BytePool APIs do not normally do aligned +/// allocation. byte_pool_alloc_aligned() first uses byte_pool_alloc() to +/// allocate a block of memory large enough to satisfy the request and +/// guarantee that a subset of the memory allocation will satisfy the +/// alignment constraint plus the overhead of a dummy block header. Note that +/// it is space-inefficient to allocate many small aligned areas. If possble +/// it would be better to allocate a single aligned area and then have the +/// application partition the memory as required. +/// +/// Memory areas allocated by byte_pool_alloc_aligned() can be freed with +/// byte_pool_free(), just like any other dynamic memory allocation. +/// +/// The part of this API that manipulates the \a pool runs as an +/// SSX_NONCRITICAL critical section. The underlying call of byte_pool_alloc() +/// uses a constant-time algorithm. +/// +/// Return values other than 0 are not necessarily errors; see \ref +/// ssx_errors. +/// +/// The following return codes are not considered errors: +/// +/// \retval 0 Success +/// +/// \retval -BYTE_POOL_NO_MEMORY The allocation request could not be +/// satisfied. The memory pointer will also be NULL (0) in this case. +/// +/// The following return codes are considered errors: +/// +/// \retval -BYTE_POOL_INVALID_OBJECT The \a pool argument was NULL (0). +/// +/// \retval -BYTE_POOL_INVALID_ARGUMENT The \a memory argument is NULL (0), or +/// the \a alignment argument is invalid. + +// The allocation must be big enough for the size requested + the alignment +// amount (to guarantee alignment) + room for a dummy ByteBlock. The dummy +// ByteBlock is marked by setting the \a next pointer to 1 to indicate that +// this is an aligned allocation. In this case the \a previous pointer of the +// dummy ByteBlock points to the ByteBlock of the original allocation. + +int +byte_pool_alloc_aligned(BytePool *pool, void **memory, size_t size, + int alignment) +{ + int rc; + unsigned long pow2_alignment, mask, aligned; + void *unaligned_memory; + ByteBlock *dummy_block, *unaligned_block; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF((alignment < 1) || (alignment >= BITS_PER_UNSIGNED_LONG), + BYTE_POOL_INVALID_ARGUMENT); + } + + pow2_alignment = (unsigned long)1 << (unsigned long)alignment; + mask = pow2_alignment - 1; + + rc = byte_pool_alloc(pool, &unaligned_memory, + size + pow2_alignment + sizeof(ByteBlock)); + + if (rc || (unaligned_memory == 0)) { + *memory = 0; + return rc; + } + unaligned_block = (ByteBlock *)(((unsigned long)unaligned_memory) - + sizeof(ByteBlock)); + + aligned = (unsigned long)unaligned_memory + sizeof(ByteBlock); + if (aligned & mask) { + aligned += (pow2_alignment - (aligned & mask)); + } + *memory = (void *)aligned; + + dummy_block = (ByteBlock *)(aligned - sizeof(ByteBlock)); + dummy_block->previous = unaligned_block; + dummy_block->next = (ByteBlock*)1; + + if (0) { + printk("byte_pool_alloc_aligned(%p, -> %p, %zu, %d)\n", + pool, *memory, size, alignment); + } + + return 0; +} + + +/// Allocate aligned memory from a byte pool and clear +/// +/// byte_pool_calloc_alligned() allocates memory using +/// byte_pool_alloc_aligned() then clears the memory area using memset(). The +/// arguments conform to the POSIX standard for calloc(). See +/// byte_pool_alloc_aligned() for return codes and usage notes. In particular +/// note that this memory must be freed with byte_pool_free_aligned(). + +int +byte_pool_calloc_aligned(BytePool *pool, void **memory, + size_t nmemb, size_t size, int alignment) +{ + int rc; + + rc = byte_pool_alloc_aligned(pool, memory, nmemb * size, alignment); + if (rc || (*memory == 0)) return rc; + + memset(*memory, 0, nmemb * size); + + return 0; +} + + +/// malloc() allocates \a size bytes and returns a pointer to the allocated +/// memory. The memory is not cleared. The value returned is a pointer to the +/// allocated memory, which is suitably aligned for any kind of variable, or +/// NULL if the requested \a size is 0 or the request fails. +/// +/// NB: The aplication must create and assign a BytePool object to the +/// library variable _malloc_byte_pool in order for malloc() to work. + +void * +malloc(size_t size) +{ + void *memory; + + if (byte_pool_alloc(_malloc_byte_pool, &memory, size)) { + memory = 0; + } + return memory; +} + + +/// calloc() allocates memory for an array of \a nmemb elements of \a size +/// bytes each and returns a pointer to the allocated memory. The memory is +/// set to zero. The value returned is a pointer to the allocated and cleared +/// memory, which is suitably aligned for any kind of variable, or NULL if the +/// requested \a size is 0 or the request fails. +/// +/// NB: The aplication must create and assign a BytePool object to the +/// library variable _malloc_byte_pool in order for calloc() to work. + +void * +calloc(size_t nmemb, size_t size) +{ + void *memory; + + if (byte_pool_calloc(_malloc_byte_pool, &memory, nmemb, size)) { + return 0; + } + return memory; +} + + +/// free() frees the memory space pointed to by \a ptr, which must have been +/// returned by a previous call to malloc(), posix_memalign, calloc() or +/// realloc(). Otherwise, or if free(ptr) has already been called before, +/// undefined behavior occurs. If \a ptr is NULL, no operation is performed. +/// +/// NB: The aplication must create and assign a BytePool object to the +/// library variable _malloc_byte_pool in order for free() to work. + +void +free(void *ptr) +{ + byte_pool_free(_malloc_byte_pool, ptr); +} + + +/// realloc() changes the size of the memory block pointed to by \a ptr to \a +/// size bytes. The contents will be unchanged to the minimum of the old and +/// new sizes; newly allocated memory will be uninitialized. If \a ptr is +/// NULL, the call is equivalent to malloc(size); if \a size is equal to zero, +/// the call is equivalent to free(ptr). Unless \a ptr is NULL, it must have +/// been returned by an earlier call to malloc(), calloc() or realloc(). If +/// the area pointed to was moved, a free(ptr) is done. +/// +/// realloc() returns a pointer to the newly allocated memory, which is +/// suitably aligned for any kind of variable and may be different from \a +/// ptr, or NULL if the request fails. If \a size was equal to 0, either NULL +/// or a pointer suitable to be passed to free() is returned. If realloc() +/// fails the original block is left untouched; it is not freed or moved. + +void* +realloc(void *ptr, size_t size) +{ + void *memory; + size_t useful_size; + + // Handle simple case + + if (ptr == 0) { + + memory = malloc(size); + + } else if (size == 0) { + + free(ptr); + memory = 0; + + } else { + + // Find out the useful size of the block. If we need more than this we + // need to allocate a new block and memcpy() the old data to the new + // block and free the old block. If we need less then this we also + // need to allocate a new block and move the head of the current + // data. If the new size is the same as the current size we do nothing. + + byte_pool_block_info(ptr, 0, 0, &useful_size); + + if (size == useful_size) { + + memory = ptr; + + } else { + + memory = malloc(size); + if (memory != 0) { + memcpy(memory, ptr, (size > useful_size) ? useful_size : size); + free(ptr); + } + } + } + return memory; +} + + +/// The posix_memalign() function allocates \a size bytes aligned on a +/// boundary specified by \a alignment, and returns a pointer to the allocated +/// memory in \a memptr. The value of \a alignment shall be a multiple of +/// sizeof(void*), that is also a power of two. Upon successful completion, +/// the value pointed to by \a memptr will be a multiple of alignment. +/// +/// Note that memory allocated with posix_memalign() can be freed with +/// free(). +/// +/// In the event of errors, the contents of \a memptr will be returned as 0. +/// +/// The following return codes are mandated by POSIX, and are always returned +/// in the event of the specified condition. +/// +/// \retval 0 Success +/// +/// \retval -EINVAL The value of the \a alignment parameter is not a power of +/// two multiple of sizeof(void*). +/// +/// \retval -ENOMEM There is insufficient memory available with the requested +/// alignment. +/// +/// The following return codes are implementation-specific and may be +/// configured to cause a kernel panic. +/// +/// \retval -BYTE_POOL_INVALID_OBJECT The \a _malloc_byte_pool is NULL (0). +/// +/// \retval -BYTE_POOL_INVALID_ARGUMENT The \a memptr argument is NULL (0). + +int +posix_memalign(void** memptr, size_t alignment, size_t size) +{ + int rc; + + if (((alignment & (alignment - 1)) != 0) || + (alignment < sizeof(void*))) { + rc = -EINVAL; + } else { + rc = byte_pool_alloc_aligned(_malloc_byte_pool, memptr, size, + floor_log2(alignment)); + if (!rc && (*memptr == 0)) { + rc = -ENOMEM; + } + } + if (rc && memptr) { + *memptr = 0; + } + return rc; +} + + +/// Print a dump of a byte pool, including the header and allocation report +/// +/// \param stream The stream to receive the dump +/// +/// \param pool The BytePool object to dump +/// +/// \bug This routine is not thread safe. + +void +byte_pool_report(FILE* stream, BytePool* pool) +{ + ByteBlock* block; + uint8_t* p8; + uint32_t* p32; + int i; + + fprintf(stream, ">>> Byte Pool Report for Pool %p <<<\n", pool); + + fprintf(stream, ">>> BytePool Object Dump <<<\n"); + +#define DUMPIT(x, fmt) \ + fprintf(stream, "%20s : " #fmt "\n", #x, pool->x) + + DUMPIT(first_row, %d); + DUMPIT(last_row, %d); + DUMPIT(columns, %d); + DUMPIT(log2_columns, %d); + DUMPIT(column_mask, 0x%08x); + DUMPIT(minimum_block_size, %d); + DUMPIT(free, %p); + DUMPIT(column_status, %p); + DUMPIT(row_status, %lu); + DUMPIT(first_block, %p); + DUMPIT(big_block, %p); + DUMPIT(last_block, %p); + DUMPIT(initial_allocation, %d); + DUMPIT(bytes_allocated, %d); + DUMPIT(bytes_free, %d); + DUMPIT(blocks_allocated, %d); + DUMPIT(blocks_free, %d); + DUMPIT(alloc_calls, %d); + DUMPIT(free_calls, %d); + + fprintf(stream, ">>> Byte Pool Allocation Report <<<\n"); + fprintf(stream, + ">>> status : address : size : binary[0:7] : ASCII[0:7] <<<\n"); + + for (block = pool->first_block->next; + block != pool->last_block; + block = block->next) { + + fprintf(stream, " %c : %p : %6zu : ", + (block_is_free(block) ? 'F' : 'A'), + block, block_size(block)); + + p8 = (uint8_t*)((unsigned long)block + sizeof(ByteBlock)); + p32 = (uint32_t*)p8; + + fprintf(stream, "0x%08x 0x%08x : ", p32[0], p32[1]); + for (i = 0; i < 8; i++) { + if (isprint(p8[i])) { + fputc(p8[i], stream); + } else { + fputc('.', stream); + } + } + fputc('\n', stream); + } + + fprintf(stream, ">>> End Report <<<\n"); +} diff --git a/src/lib/ppc405lib/byte_pool.h b/src/lib/ppc405lib/byte_pool.h new file mode 100644 index 0000000..8766745 --- /dev/null +++ b/src/lib/ppc405lib/byte_pool.h @@ -0,0 +1,166 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/byte_pool.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __BYTE_POOL_H__ +#define __BYTE_POOL_H__ + +// $Id$ + +#ifndef __ASSEMBLER__ + +#include "ssx_io.h" + +struct ByteBlock; +struct FreeByteBlock; + +// A handy constant + +#define BITS_PER_UNSIGNED_LONG (8 * sizeof(unsigned long)) + + +/// A control structure for a byte pool. The application will never modify the +/// structure fields directly, but some applications may be interested in +/// reading the statistics. + +typedef struct { + + // The index of the first allocated row of free list pointers + int first_row; + + // The index of the last allocated row of free list pointers + int last_row; + + // The number of columns in each row of free list pointers + int columns; + + // The log2 of the number of columns in each row of free list pointers + int log2_columns; + + // The shifted block size is ANDed with this mask to extract the column + // number. + size_t column_mask; + + // The minimum block size. + int minimum_block_size; + + // The vectors of free list pointers + struct FreeByteBlock **free[BITS_PER_UNSIGNED_LONG]; + + // The array of column status bit masks + unsigned long column_status[BITS_PER_UNSIGNED_LONG]; + + // The row status bit mask + unsigned long row_status; + + // A sentinel node - the first allocated block. Kept for error checking + // purposes. + struct ByteBlock *first_block; + + // The initial memory allocation. Kept here only for debugging purposes. + struct ByteBlock *big_block; + + // A sentinel node - the last allocated block. Kept here for error + // checking purposes. + struct ByteBlock *last_block; + + // The initial allocation. Kept here for debugging and statistics. + size_t initial_allocation; + + // The total number of bytes currently allocated (excludes overhead) + size_t bytes_allocated; + + // The total number of bytes currently free in the pool + size_t bytes_free; + + // The total number of blocks allocated from the pool + size_t blocks_allocated; + + // The total number of blocks free in the pool + size_t blocks_free; + + // The number of calls to allocate memory + size_t alloc_calls; + + // The number of calls to free memory + size_t free_calls; + +} BytePool; + +extern BytePool *_malloc_byte_pool; + +int +byte_pool_create(BytePool *pool, void *memory, size_t size); + +int +byte_pool_create_tuned(BytePool *pool, void *memory, size_t size, + int columns); + +int +byte_pool_alloc(BytePool *pool, void **memory, size_t size); + +int +byte_pool_calloc(BytePool *pool, void **memory, size_t nmemb, size_t size); + +int +byte_pool_free(BytePool *pool, void *memory); + +void +byte_pool_block_info(void* memory, + void** actual_address, size_t* actual_size, + size_t* useful_size); + +int +byte_pool_alloc_aligned(BytePool *pool, void **memory, size_t size, + int alignment); + +void * +malloc(size_t size); + +void * +calloc(size_t nmemb, size_t size); + +void +free(void *ptr); + +int +posix_memalign(void** memptr, size_t alignment, size_t size); + +void +byte_pool_report(FILE* stream, BytePool* pool); + +#endif /* __ASSEMBLER__ */ + + +// Error/panic codes + +#define BYTE_POOL_INVALID_OBJECT 0x00b98e01 +#define BYTE_POOL_INVALID_ARGUMENT 0x00b98e02 +#define BYTE_POOL_REVERSE_LINKAGE 0x00b98e03 +#define BYTE_POOL_FORWARD_LINKAGE 0x00b98e04 +#define BYTE_POOL_NO_MEMORY 0x00b98e05 + +#define BYTE_POOL_NULL_FREE_LIST 0x00b98e10 +#define BYTE_POOL_INVALID_FREE_LIST 0x00b98e11 + +#endif /* __BYTE_POOL_H__ */ diff --git a/src/lib/ppc405lib/chip_config.h b/src/lib/ppc405lib/chip_config.h new file mode 100644 index 0000000..cc772e2 --- /dev/null +++ b/src/lib/ppc405lib/chip_config.h @@ -0,0 +1,109 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/chip_config.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __CHIP_CONFIG_H__ +#define __CHIP_CONFIG_H__ + +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2014 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file chip_config.h +/// \brief Chip configuration data structures for OCC procedures + +#ifndef __ASSEMBLER__ + +#include <stdint.h> + +/// A bitmask defining a chip configuration +/// +/// Since we are using the conventional big-endian notation, any use of these +/// bitmasks requires that the data being tested is of this type - otherwise +/// the masks won't work. +/// +/// Layout: +/// +/// Bits 0:15 - Core chiplet 0..15 is configured +/// Bits 16:23 - MCS 0..7 is configured +/// Bits 24:31 - Centaur 0..7 is configured + +typedef uint64_t ChipConfig; +typedef uint16_t ChipConfigCores; +typedef uint8_t ChipConfigMcs; +typedef uint8_t ChipConfigCentaur; + + +/// Convert a ChipConfig into a mask suitable for use as the 32-bit chiplet +/// mask argument of a PORE wakeup program. +#if 0 +static inline uint32_t +pore_exe_mask(ChipConfig config) +{ + return (uint32_t)((config >> 32) & 0xffff0000); +} +#endif + +/// Left justify and mask core chiplet configuration into a uint32_t + +static inline uint32_t +left_justify_core_config(ChipConfig config) +{ + return (uint32_t)((config >> 32) & 0xffff0000); +} + +/// Left justify and mask MCS configuration into a uint32_t + +static inline uint32_t +left_justify_mcs_config(ChipConfig config) +{ + return (uint32_t)((config >> 16) & 0xff000000); +} + +/// Left justify and mask Centaur configuration into a uint32_t + +static inline uint32_t +left_justify_centaur_config(ChipConfig config) +{ + return (uint32_t)((config >> 8) & 0xff000000); +} + +#endif // __ASSEMBLER__ + + +#define CHIP_CONFIG_CORE_BASE 0 +#define CHIP_CONFIG_CORE(n) \ + ((0x8000000000000000ull >> CHIP_CONFIG_CORE_BASE) >> (n)) + +#define CHIP_CONFIG_MCS_BASE 16 +#define CHIP_CONFIG_MCS(n) \ + ((0x8000000000000000ull >> CHIP_CONFIG_MCS_BASE) >> (n)) + +#define CHIP_CONFIG_CENTAUR_BASE 24 +#define CHIP_CONFIG_CENTAUR(n) \ + ((0x8000000000000000ull >> CHIP_CONFIG_CENTAUR_BASE) >> (n)) + + +#endif /* __CHIP_CONFIG_H__ */ diff --git a/src/lib/ppc405lib/ctype.c b/src/lib/ppc405lib/ctype.c new file mode 100644 index 0000000..5069590 --- /dev/null +++ b/src/lib/ppc405lib/ctype.c @@ -0,0 +1,46 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/ctype.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id: ctype.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/ctype.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ctype.c +/// \brief Replacement for <ctype.h> functions +/// +/// This file contains entry point equivalents for the "ctype.h" macros. +/// These would only ever be used by assembler programs, therefore it's likely +/// that the object file will never be linked into an image. + +#define __CTYPE_C__ +#include "ctype.h" +#undef __CTYPE_C__ + + + + diff --git a/src/lib/ppc405lib/ctype.h b/src/lib/ppc405lib/ctype.h new file mode 100644 index 0000000..9e41e45 --- /dev/null +++ b/src/lib/ppc405lib/ctype.h @@ -0,0 +1,149 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/ctype.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __CTYPE_H__ +#define __CTYPE_H__ + +// $Id: ctype.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/ctype.h,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ctype.h +/// \brief Replacement for <ctype.h> +/// +/// The Gnu <ctype.h> requires some locale information to be defined. We +/// avoid this overhead and implement the simple functions, simply assuming +/// standard 8-bit ASCII. The standard requires that these be defined as entry +/// points, but may be defined as macros, therefore all macros defined by +/// ctype.h are also be replicated in \c ctype.c + +// The reference for which characters are included in each set was: +// +// http://www.cplusplus.com/reference/clibrary/cctype/ +// +// Note that no code was copied from the above or any other published +// description of the ctype.h functionality. + +// To keep space to a minimum we encode the 8 common types directly into +// an 8-bit mask. Other types take a little longer to compute. + +#define _CTYPE_CNTRL 0x01 +#define _CTYPE_SPACE 0x02 +#define _CTYPE_PRINT 0x04 +#define _CTYPE_PUNCT 0x08 +#define _CTYPE_UPPER 0x10 +#define _CTYPE_LOWER 0x20 +#define _CTYPE_DIGIT 0x40 +#define _CTYPE_XDIGIT 0x80 + +#ifndef __ASSEMBLER__ + +#include "stdint.h" + +/// \bug <ctype.h> can not include <stdio.h> to get the definition of +/// EOF. This is because it causes conflicts withe the pore_inline* code which +/// is portable (and ported) to Linux and PHYP. We need to go back through the +/// way the includes are done in SSX and this library and fix this. We should +/// have redefined <stdio.h> rather than creating a new "ssx_io.h" + +#define _CTYPE_EOF -1 + +// Note that in all of the type macros, 'c' is an unsigned char. + +extern const uint8_t _ctype[256]; + +#define _CTYPE_ISLOWER(c) (_ctype[c] & _CTYPE_LOWER) + +#define _CTYPE_ISUPPER(c) (_ctype[c] & _CTYPE_UPPER) + +#define _CTYPE_ISALPHA(c) (_CTYPE_ISUPPER(c) || _CTYPE_ISLOWER(c)) + +#define _CTYPE_ISDIGIT(c) (_ctype[c] & _CTYPE_DIGIT) + +#define _CTYPE_ISALNUM(c) (_CTYPE_ISALPHA(c) || _CTYPE_ISDIGIT(c)) + +#define _CTYPE_ISXDIGIT(c) (_ctype[c] & _CTYPE_XDIGIT) + +#define _CTYPE_ISCNTRL(c) (_ctype[c] & _CTYPE_CNTRL) + +#define _CTYPE_ISSPACE(c) (_ctype[c] & _CTYPE_SPACE) + +#define _CTYPE_ISPRINT(c) (_ctype[c] & _CTYPE_PRINT) + +#define _CTYPE_ISGRAPH(c) (_CTYPE_ISPRINT(c) && ((c) != 0x20)) + +#define _CTYPE_ISPUNCT(c) (_ctype[c] & _CTYPE_PUNCT) + +#define _CTYPE_TOUPPER(c) (islower(c) ? ((c) + ('A' - 'a')) : (c)) + +#define _CTYPE_TOLOWER(c) (isupper(c) ? ((c) - ('A' - 'a')) : (c)) + +// When #include'ed into ctype.c, the non-inline forms of the functions are +// created. +// +// Note that the specification requires that 'c' "must have the value of an +// unsigned char or EOF". The specification also stipulates that "The values +// returned are non-zero if the character c falls into the tested class, and a +// zero value if not." + +#ifdef __CTYPE_C__ +#define _CTYPE_EXTERN_INLINE +#else +#define _CTYPE_EXTERN_INLINE extern inline +#endif + +#define _CTYPE_PREDICATE(predicate, def) \ + _CTYPE_EXTERN_INLINE int predicate(int c) { \ + return ((c == _CTYPE_EOF) ? \ + 0 : _CTYPE_##def((unsigned char)c)); \ + } + +#define _CTYPE_FUNCTION(function, def) \ + _CTYPE_EXTERN_INLINE int function(int c) { \ + return ((c == _CTYPE_EOF) ? \ + _CTYPE_EOF : _CTYPE_##def((unsigned char)c)); \ + } + +_CTYPE_PREDICATE(islower, ISLOWER) +_CTYPE_PREDICATE(isupper, ISUPPER) +_CTYPE_PREDICATE(isalpha, ISALPHA) +_CTYPE_PREDICATE(isdigit, ISDIGIT) +_CTYPE_PREDICATE(isalnum, ISALNUM) +_CTYPE_PREDICATE(isxdigit, ISXDIGIT) +_CTYPE_PREDICATE(iscntrl, ISCNTRL) +_CTYPE_PREDICATE(isspace, ISSPACE) +_CTYPE_PREDICATE(isprint, ISPRINT) +_CTYPE_PREDICATE(isgraph, ISGRAPH) +_CTYPE_PREDICATE(ispunct, ISPUNCT) + +_CTYPE_FUNCTION(tolower, TOLOWER) +_CTYPE_FUNCTION(toupper, TOUPPER) + +#endif // __ASSEMBLER__ + +#endif /* __CTYPE_H__ */ diff --git a/src/lib/ppc405lib/ctype_table.c b/src/lib/ppc405lib/ctype_table.c new file mode 100644 index 0000000..01dd08a --- /dev/null +++ b/src/lib/ppc405lib/ctype_table.c @@ -0,0 +1,302 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/ctype_table.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id: ctype_table.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/ctype_table.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ctype_table.c +/// \brief Character table for <ctype.h> functions. +/// +/// This table is used by the <ctype.h> functions for a quick lookup of +/// character type information. Because the true functional forms of <ctype.h> +/// functions are likely never required, but this file is always required, it +/// is stored separately from ctype.c to (slightly) reduce code/data space +/// requirements. + +#include <stdint.h> +#include <ctype.h> + +const uint8_t _ctype[256] = { + _CTYPE_CNTRL, /* 0 00 NUL Null char */ + _CTYPE_CNTRL, /* 1 01 SOH Start of Heading */ + _CTYPE_CNTRL, /* 2 02 STX Start of Text */ + _CTYPE_CNTRL, /* 3 03 ETX End of Text */ + _CTYPE_CNTRL, /* 4 04 EOT End of Transmission */ + _CTYPE_CNTRL, /* 5 05 ENQ Enquiry */ + _CTYPE_CNTRL, /* 6 06 ACK Acknowledgment */ + _CTYPE_CNTRL, /* 7 07 BEL Bell */ + _CTYPE_CNTRL, /* 8 08 BS Back Space */ + _CTYPE_CNTRL | _CTYPE_SPACE, /* 9 09 HT Horizontal Tab */ + _CTYPE_CNTRL | _CTYPE_SPACE, /* 10 0A LF Line Feed */ + _CTYPE_CNTRL | _CTYPE_SPACE, /* 11 0B VT Vertical Tab */ + _CTYPE_CNTRL | _CTYPE_SPACE, /* 12 0C FF Form Feed */ + _CTYPE_CNTRL | _CTYPE_SPACE, /* 13 0D CR Carriage Return */ + _CTYPE_CNTRL, /* 14 0E SO Shift Out / X-On */ + _CTYPE_CNTRL, /* 15 0F SI Shift In / X-Off */ + 0, /* 16 10 DLE Data Line Escape */ + 0, /* 17 11 DC1 Device Control 1 (oft. XON) */ + 0, /* 18 12 DC2 Device Control 2 */ + 0, /* 19 13 DC3 Device Control 3 (oft. XOFF) */ + 0, /* 20 14 DC4 Device Control 4 */ + 0, /* 21 15 NAK Negative Acknowledgement */ + 0, /* 22 16 SYN Synchronous Idle */ + 0, /* 23 17 ETB End of Transmit Block */ + 0, /* 24 18 CAN Cancel */ + 0, /* 25 19 EM End of Medium */ + 0, /* 26 1A SUB Substitute */ + 0, /* 27 1B ESC Escape */ + 0, /* 28 1C FS File Separator */ + 0, /* 29 1D GS Group Separator */ + 0, /* 30 1E RS Record Separator */ + 0, /* 31 1F US Unit Separator */ + _CTYPE_PRINT | _CTYPE_SPACE, /* 32 20 Space */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 33 21 ! Exclamation mark */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 34 22 " Double quotes (or speech marks) */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 35 23 # Number */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 36 24 $ Dollar */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 37 25 % Procenttecken */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 38 26 & Ampersand */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 39 27 ' Single quote */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 40 28 ( Open parenthesis (or open bracket) */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 41 29 ) Close parenthesis (or close bracket) */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 42 2A * Asterisk */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 43 2B + Plus */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 44 2C , Comma */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 45 2D - Hyphen */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 46 2E . Period, dot or full stop */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 47 2F / Slash or divide */ + _CTYPE_PRINT | _CTYPE_DIGIT, /* 48 30 0 Zero */ + _CTYPE_PRINT | _CTYPE_DIGIT, /* 49 31 1 One */ + _CTYPE_PRINT | _CTYPE_DIGIT, /* 50 32 2 Two */ + _CTYPE_PRINT | _CTYPE_DIGIT, /* 51 33 3 Three */ + _CTYPE_PRINT | _CTYPE_DIGIT, /* 52 34 4 Four */ + _CTYPE_PRINT | _CTYPE_DIGIT, /* 53 35 5 Five */ + _CTYPE_PRINT | _CTYPE_DIGIT, /* 54 36 6 Six */ + _CTYPE_PRINT | _CTYPE_DIGIT, /* 55 37 7 Seven */ + _CTYPE_PRINT | _CTYPE_DIGIT, /* 56 38 8 Eight */ + _CTYPE_PRINT | _CTYPE_DIGIT, /* 57 39 9 Nine */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 58 3A : Colon */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 59 3B ; Semicolon */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 60 3C < Less than (or open angled bracket) */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 61 3D = Equals */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 62 3E > Greater than (or close angled bracket) */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 63 3F ? Question mark */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 64 40 @ At symbol */ + _CTYPE_PRINT | _CTYPE_UPPER | _CTYPE_XDIGIT, /* 65 41 A Uppercase A */ + _CTYPE_PRINT | _CTYPE_UPPER | _CTYPE_XDIGIT, /* 66 42 B Uppercase B */ + _CTYPE_PRINT | _CTYPE_UPPER | _CTYPE_XDIGIT, /* 67 43 C Uppercase C */ + _CTYPE_PRINT | _CTYPE_UPPER | _CTYPE_XDIGIT, /* 68 44 D Uppercase D */ + _CTYPE_PRINT | _CTYPE_UPPER | _CTYPE_XDIGIT, /* 69 45 E Uppercase E */ + _CTYPE_PRINT | _CTYPE_UPPER | _CTYPE_XDIGIT, /* 70 46 F Uppercase F */ + _CTYPE_PRINT | _CTYPE_UPPER, /* 71 47 G Uppercase G */ + _CTYPE_PRINT | _CTYPE_UPPER, /* 72 48 H Uppercase H */ + _CTYPE_PRINT | _CTYPE_UPPER, /* 73 49 I Uppercase I */ + _CTYPE_PRINT | _CTYPE_UPPER, /* 74 4A J Uppercase J */ + _CTYPE_PRINT | _CTYPE_UPPER, /* 75 4B K Uppercase K */ + _CTYPE_PRINT | _CTYPE_UPPER, /* 76 4C L Uppercase L */ + _CTYPE_PRINT | _CTYPE_UPPER, /* 77 4D M Uppercase M */ + _CTYPE_PRINT | _CTYPE_UPPER, /* 78 4E N Uppercase N */ + _CTYPE_PRINT | _CTYPE_UPPER, /* 79 4F O Uppercase O */ + _CTYPE_PRINT | _CTYPE_UPPER, /* 80 50 P Uppercase P */ + _CTYPE_PRINT | _CTYPE_UPPER, /* 81 51 Q Uppercase Q */ + _CTYPE_PRINT | _CTYPE_UPPER, /* 82 52 R Uppercase R */ + _CTYPE_PRINT | _CTYPE_UPPER, /* 83 53 S Uppercase S */ + _CTYPE_PRINT | _CTYPE_UPPER, /* 84 54 T Uppercase T */ + _CTYPE_PRINT | _CTYPE_UPPER, /* 85 55 U Uppercase U */ + _CTYPE_PRINT | _CTYPE_UPPER, /* 86 56 V Uppercase V */ + _CTYPE_PRINT | _CTYPE_UPPER, /* 87 57 W Uppercase W */ + _CTYPE_PRINT | _CTYPE_UPPER, /* 88 58 X Uppercase X */ + _CTYPE_PRINT | _CTYPE_UPPER, /* 89 59 Y Uppercase Y */ + _CTYPE_PRINT | _CTYPE_UPPER, /* 90 5A Z Uppercase Z */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 91 5B [ Opening bracket */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 92 5C \ Backslash */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 93 5D ] Closing bracket */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 94 5E ^ Caret - circumflex */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 95 5F _ Underscore */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 96 60 ` Grave accent */ + _CTYPE_PRINT | _CTYPE_LOWER | _CTYPE_XDIGIT, /* 97 61 a Lowercase a */ + _CTYPE_PRINT | _CTYPE_LOWER | _CTYPE_XDIGIT, /* 98 62 b Lowercase b */ + _CTYPE_PRINT | _CTYPE_LOWER | _CTYPE_XDIGIT, /* 99 63 c Lowercase c */ + _CTYPE_PRINT | _CTYPE_LOWER | _CTYPE_XDIGIT, /* 100 64 d Lowercase d */ + _CTYPE_PRINT | _CTYPE_LOWER | _CTYPE_XDIGIT, /* 101 65 e Lowercase e */ + _CTYPE_PRINT | _CTYPE_LOWER | _CTYPE_XDIGIT, /* 102 66 f Lowercase f */ + _CTYPE_PRINT | _CTYPE_LOWER, /* 103 67 g Lowercase g */ + _CTYPE_PRINT | _CTYPE_LOWER, /* 104 68 h Lowercase h */ + _CTYPE_PRINT | _CTYPE_LOWER, /* 105 69 i Lowercase i */ + _CTYPE_PRINT | _CTYPE_LOWER, /* 106 6A j Lowercase j */ + _CTYPE_PRINT | _CTYPE_LOWER, /* 107 6B k Lowercase k */ + _CTYPE_PRINT | _CTYPE_LOWER, /* 108 6C l Lowercase l */ + _CTYPE_PRINT | _CTYPE_LOWER, /* 109 6D m Lowercase m */ + _CTYPE_PRINT | _CTYPE_LOWER, /* 110 6E n Lowercase n */ + _CTYPE_PRINT | _CTYPE_LOWER, /* 111 6F o Lowercase o */ + _CTYPE_PRINT | _CTYPE_LOWER, /* 112 70 p Lowercase p */ + _CTYPE_PRINT | _CTYPE_LOWER, /* 113 71 q Lowercase q */ + _CTYPE_PRINT | _CTYPE_LOWER, /* 114 72 r Lowercase r */ + _CTYPE_PRINT | _CTYPE_LOWER, /* 115 73 s Lowercase s */ + _CTYPE_PRINT | _CTYPE_LOWER, /* 116 74 t Lowercase t */ + _CTYPE_PRINT | _CTYPE_LOWER, /* 117 75 u Lowercase u */ + _CTYPE_PRINT | _CTYPE_LOWER, /* 118 76 v Lowercase v */ + _CTYPE_PRINT | _CTYPE_LOWER, /* 119 77 w Lowercase w */ + _CTYPE_PRINT | _CTYPE_LOWER, /* 120 78 x Lowercase x */ + _CTYPE_PRINT | _CTYPE_LOWER, /* 121 79 y Lowercase y */ + _CTYPE_PRINT | _CTYPE_LOWER, /* 122 7A z Lowercase z */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 123 7B { Opening brace */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 124 7C | Vertical bar */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 125 7D } Closing brace */ + _CTYPE_PRINT | _CTYPE_PUNCT, /* 126 7E ~ Equivalency sign - tilde */ + 0, /* 127 7F Delete */ + 0, /* 128 80 Euro sign */ + 0, /* 129 81 */ + 0, /* 130 82 ' Single low-9 quotation mark */ + 0, /* 131 83 Latin small letter f with hook */ + 0, /* 132 84 " Double low-9 quotation mark */ + 0, /* 133 85 Horizontal ellipsis */ + 0, /* 134 86 Dagger */ + 0, /* 135 87 Double dagger */ + 0, /* 136 88 Modifier letter circumflex accent */ + 0, /* 137 89 Per mille sign */ + 0, /* 138 8A Latin capital letter S with caron */ + 0, /* 139 8B Single left-pointing angle quotation */ + 0, /* 140 8C Latin capital ligature OE */ + 0, /* 141 8D */ + 0, /* 142 8E Latin captial letter Z with caron */ + 0, /* 143 8F */ + 0, /* 144 90 */ + 0, /* 145 91 ' Left single quotation mark */ + 0, /* 146 92 ' Right single quotation mark */ + 0, /* 147 93 " Left double quotation mark */ + 0, /* 148 94 " Right double quotation mark */ + 0, /* 149 95 Bullet */ + 0, /* 150 96 En dash */ + 0, /* 151 97 Em dash */ + 0, /* 152 98 Small tilde */ + 0, /* 153 99 Trade mark sign */ + 0, /* 154 9A Latin small letter S with caron */ + 0, /* 155 9B Single right-pointing angle quotation mark */ + 0, /* 156 9C Latin small ligature oe */ + 0, /* 157 9D */ + 0, /* 158 9E Latin small letter z with caron */ + 0, /* 159 9F Latin capital letter Y with diaeresis */ + 0, /* 160 A0 Non-breaking space */ + 0, /* 161 A1 Inverted exclamation mark */ + 0, /* 162 A2 Cent sign */ + 0, /* 163 A3 Pound sign */ + 0, /* 164 A4 Currency sign */ + 0, /* 165 A5 Yen sign */ + 0, /* 166 A6 Pipe, Broken vertical bar */ + 0, /* 167 A7 Section sign */ + 0, /* 168 A8 Spacing diaeresis - umlaut */ + 0, /* 169 A9 Copyright sign */ + 0, /* 170 AA Feminine ordinal indicator */ + 0, /* 171 AB Left double angle quotes */ + 0, /* 172 AC Not sign */ + 0, /* 173 AD Soft hyphen */ + 0, /* 174 AE Registered trade mark sign */ + 0, /* 175 AF Spacing macron - overline */ + 0, /* 176 B0 Degree sign */ + 0, /* 177 B1 Plus-or-minus sign */ + 0, /* 178 B2 Superscript two - squared */ + 0, /* 179 B3 Superscript three - cubed */ + 0, /* 180 B4 Acute accent - spacing acute */ + 0, /* 181 B5 Micro sign */ + 0, /* 182 B6 Pilcrow sign - paragraph sign */ + 0, /* 183 B7 Middle dot - Georgian comma */ + 0, /* 184 B8 Spacing cedilla */ + 0, /* 185 B9 Superscript one */ + 0, /* 186 BA Masculine ordinal indicator */ + 0, /* 187 BB Right double angle quotes */ + 0, /* 188 BC one quarter */ + 0, /* 189 BD Fraction one half */ + 0, /* 190 BE Fraction three quarters */ + 0, /* 191 BF Inverted question mark */ + 0, /* 192 C0 Latin capital letter A with grave */ + 0, /* 193 C1 Latin capital letter A with acute */ + 0, /* 194 C2 Latin capital letter A with circumflex */ + 0, /* 195 C3 Latin capital letter A with tilde */ + 0, /* 196 C4 Latin capital letter A with diaeresis */ + 0, /* 197 C5 Latin capital letter A with ring above */ + 0, /* 198 C6 Latin capital letter AE */ + 0, /* 199 C7 Latin capital letter C with cedilla */ + 0, /* 200 C8 Latin capital letter E with grave */ + 0, /* 201 C9 Latin capital letter E with acute */ + 0, /* 202 CA Latin capital letter E with circumflex */ + 0, /* 203 CB Latin capital letter E with diaeresis */ + 0, /* 204 CC Latin capital letter I with grave */ + 0, /* 205 CD Latin capital letter I with acute */ + 0, /* 206 CE Latin capital letter I with circumflex */ + 0, /* 207 CF Latin capital letter I with diaeresis */ + 0, /* 208 D0 Latin capital letter ETH */ + 0, /* 209 D1 Latin capital letter N with tilde */ + 0, /* 210 D2 Latin capital letter O with grave */ + 0, /* 211 D3 Latin capital letter O with acute */ + 0, /* 212 D4 Latin capital letter O with circumflex */ + 0, /* 213 D5 Latin capital letter O with tilde */ + 0, /* 214 D6 Latin capital letter O with diaeresis */ + 0, /* 215 D7 Multiplication sign */ + 0, /* 216 D8 Latin capital letter O with slash */ + 0, /* 217 D9 Latin capital letter U with grave */ + 0, /* 218 DA Latin capital letter U with acute */ + 0, /* 219 DB Latin capital letter U with circumflex */ + 0, /* 220 DC Latin capital letter U with diaeresis */ + 0, /* 221 DD Latin capital letter Y with acute */ + 0, /* 222 DE Latin capital letter THORN */ + 0, /* 223 DF Latin small letter sharp s - ess-zed */ + 0, /* 224 E0 Latin small letter a with grave */ + 0, /* 225 E1 Latin small letter a with acute */ + 0, /* 226 E2 Latin small letter a with circumflex */ + 0, /* 227 E3 Latin small letter a with tilde */ + 0, /* 228 E4 Latin small letter a with diaeresis */ + 0, /* 229 E5 Latin small letter a with ring above */ + 0, /* 230 E6 Latin small letter ae */ + 0, /* 231 E7 Latin small letter c with cedilla */ + 0, /* 232 E8 Latin small letter e with grave */ + 0, /* 233 E9 Latin small letter e with acute */ + 0, /* 234 EA Latin small letter e with circumflex */ + 0, /* 235 EB Latin small letter e with diaeresis */ + 0, /* 236 EC Latin small letter i with grave */ + 0, /* 237 ED Latin small letter i with acute */ + 0, /* 238 EE Latin small letter i with circumflex */ + 0, /* 239 EF Latin small letter i with diaeresis */ + 0, /* 240 F0 Latin small letter eth */ + 0, /* 241 F1 Latin small letter n with tilde */ + 0, /* 242 F2 Latin small letter o with grave */ + 0, /* 243 F3 Latin small letter o with acute */ + 0, /* 244 F4 Latin small letter o with circumflex */ + 0, /* 245 F5 Latin small letter o with tilde */ + 0, /* 246 F6 Latin small letter o with diaeresis */ + 0, /* 247 F7 Division sign */ + 0, /* 248 F8 Latin small letter o with slash */ + 0, /* 249 F9 Latin small letter u with grave */ + 0, /* 250 FA Latin small letter u with acute */ + 0, /* 251 FB Latin small letter u with circumflex */ + 0, /* 252 FC Latin small letter u with diaeresis */ + 0, /* 253 FD Latin small letter y with acute */ + 0, /* 254 FE Latin small letter thorn */ + 0, /* 255 FF Latin small letter y with diaeresis */ +}; diff --git a/src/lib/ppc405lib/errno.h b/src/lib/ppc405lib/errno.h new file mode 100644 index 0000000..e5ac6a9 --- /dev/null +++ b/src/lib/ppc405lib/errno.h @@ -0,0 +1,49 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/errno.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __ERRNO_H__ +#define __ERRNO_H__ + +// $Id: errno.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/errno.h,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file errno.h +/// \brief Replacement for <errno.h> +/// +/// SSX does not support a per-thread or global 'errno'. The standard Unix +/// errno values returned by library functions are defined here. The prefix +/// code is the 'telephone code' for "errn". + +#define EINVAL 0x00377601 +#define EBADF 0x00377602 +#define EAGAIN 0x00377603 +#define ENXIO 0x00377604 +#define ENOMEM 0x00377605 + +#endif /* __ERRNO_H__ */ diff --git a/src/lib/ppc405lib/fgetc.c b/src/lib/ppc405lib/fgetc.c new file mode 100644 index 0000000..6e6a1f9 --- /dev/null +++ b/src/lib/ppc405lib/fgetc.c @@ -0,0 +1,112 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/fgetc.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id: fgetc.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/fgetc.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file fgetc.c +/// \brief Implementation of fgetc() and ungetc() +/// +/// The implementations of these APIs are split out to save code space for +/// applications that do not require them. + +#include "ssx_io.h" + + +/// Read a character from a stream +/// +/// fgetc() reads the next character from \a stream and returns it as an +/// unsigned char cast to an int, or EOF on end of file or error. + +int +fgetc(FILE* stream) +{ + unsigned char c; + size_t read; + int rc; + + if (stream->flags & SSX_FILE_HAS_CHARACTER) { + stream->flags &= ~SSX_FILE_HAS_CHARACTER; + rc = stream->character; + } else { + rc = sread(stream, &c, 1, &read); + if (rc || (read != 1)) { + rc = EOF; + } else { + rc = c; + if (c == '\n') { + stream->lines++; + } + } + } + return rc; +} + + +/// Push a character back onto a stream +/// +/// ungetc() pushes \a c back to \a stream, cast to unsigned char, where it is +/// available for subsequent fgetc() operations. Only one pushback is +/// implemented. A call of ungetc() on a stream that already has a character +/// pushed back will drop the new push-back and return EOF. Otherwise +/// ungetc() returns \a c. + +int +ungetc(int c, FILE* stream) +{ + int rc; + + if (stream->flags & SSX_FILE_HAS_CHARACTER) { + rc = EOF; + } else { + stream->flags |= SSX_FILE_HAS_CHARACTER; + stream->character = c; + rc = c; + } + return rc; +} + +/// Return the number of newline characters read from a stream +/// +/// This API is an SSX entension to the \<stdio\> APIs. It returns the number +/// of newline characters read from the stream using fgetc(). Newline +/// characters read via direct calls to sread() in the stream are not counted. +/// +/// An application that sees an error while reading from a stream can print +/// flines() or flines() + 1 (depending on the application) to help users +/// track down errors in their input. +size_t +flines(FILE* stream) +{ + return stream->lines; +} + + + + diff --git a/src/lib/ppc405lib/initcall.c b/src/lib/ppc405lib/initcall.c new file mode 100644 index 0000000..664fb6e --- /dev/null +++ b/src/lib/ppc405lib/initcall.c @@ -0,0 +1,70 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/initcall.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id$ + +/// \file initcall.c +/// \brief An anonymous early initialization facility for SSX applications + +#include "ssx.h" +#include "initcall.h" + +// These linker symbols must be defined if the initcall facility is used. The +// special ELF section .data.initcall contains an array of Initcall structures +// for all declared initcalls. + +extern InitCall _INITCALL_SECTION_BASE[]; +extern SsxLinkerSymbol _INITCALL_SECTION_SIZE; + +void +_initcall_run(InitCall* initcall) +{ + void (*f)(void* arg); + + f = initcall->initcall; + if (f) { + initcall->initcall = 0; + f(initcall->arg); + } +} + + +void +initcall_run_all() +{ + InitCall* initcall; + size_t nCalls; + + initcall = _INITCALL_SECTION_BASE; + nCalls = (size_t)(&_INITCALL_SECTION_SIZE) / sizeof(InitCall); + + for (; nCalls--; initcall++) { + _initcall_run(initcall); + } +} + + + + + diff --git a/src/lib/ppc405lib/initcall.h b/src/lib/ppc405lib/initcall.h new file mode 100644 index 0000000..af8b53c --- /dev/null +++ b/src/lib/ppc405lib/initcall.h @@ -0,0 +1,116 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/initcall.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __INITCALL_H__ +#define __INITCALL_H__ + +// $Id$ + +/// \file initcall.h +/// \brief An early initialization facility for SSX applications +/// +/// The C language standard does not define a generic load-time initialization +/// method, unlike C++ which supports load-time initialization of static +/// objects. The \e initcall facility implements a simple method for SSX +/// applications to declare early initialization functions that are executed +/// prior to or during the invocation of main(). +/// +/// An \e initcall can be any function with the prototype +/// +/// \code +/// +/// void (*initcall)(void* arg) +/// +/// \endcode +/// +/// Initcalls are declared with the INITCALL() macro. An initcall is +/// represented by a named structure, and typically an initcall will be +/// declared static to the compilation unit that implements the initcall: +/// +/// \code +/// +/// void (*init_fn)(void* arg); +/// void* init_data = ...; +/// static INITCALL(init_var, init_fn, init_data); +/// +/// \endcode +/// +/// All INITCALLS loaded in the executable image are executed by the +/// initcall_run_all() API. An SSX application will typically call +/// initcall_run_all() in the function declared as the \a ssx_main_hook, or in +/// the main() routine itself. +/// +/// Initcalls are run in an arbitrary order. However if initcall \a b is +/// dependent on initcall \a a, then initcall \a b can execute +/// initcall_run(&a) to guarantee that initcall \a a runs before \a b. +/// Regardless, every initcall is run exectly once by the initcall facility, +/// even if initcall_run() or initcall_run_all() were to be used multiple +/// times. +/// +/// Behind the scenes, initcalls are implemented by a special ELF section, +/// .data.initcall, that records all declared initcalls. The +/// initcall_run_all() API simply runs all initcalls declared in +/// .data.initcall. + +/// The structure representing an initcall + +typedef struct { + + /// The initialization function + /// + /// Prior to running the initcall, this field is zeroed. This guarantess + /// that each initcall is run at most 1 time. + void (*initcall)(void* arg); + + /// The argument to the initialization function + void* arg; + +} InitCall; + + +/// Declare an initcall +/// +/// This macro generates C code and global data so must be placed at file +/// scope in a C file, not in a header file or inside a C function +/// body. Unless the initcall needs to be referenced by another initcall (to +/// guarantee ordering), this declaration will normally be prepended with +/// 'static'. +#define INITCALL(_var, _initcall, _arg) \ + InitCall _var __attribute__ ((used, section (".data.initcall"))) = \ + {.initcall = _initcall, .arg = _arg}; + + +/// Run the initcall represented by an InitCall structure, assuming it has not +/// already run. +/// +/// \param[in] i_initcall The address of the initcall structure to run +void +initcall_run(InitCall* i_initcall); + + +/// Run all initcalls +void +initcall_run_all(); + +#endif // __INITCALL_H__ diff --git a/src/lib/ppc405lib/lfsr.c b/src/lib/ppc405lib/lfsr.c new file mode 100644 index 0000000..21ddb93 --- /dev/null +++ b/src/lib/ppc405lib/lfsr.c @@ -0,0 +1,50 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/lfsr.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id$ + +/// \file lfsr.c +/// \brief + +#include <stdint.h> +#include "lfsr.h" + +// Parity for 4-bit numbers +static uint8_t S_parity4[16] = { + 0, 1, 1, 0, + 1, 0, 0, 1, + 1, 0, 0, 1, + 0, 1, 1, 0 +}; + +// 64, 63, 61, 60 LFSR. The routine is coded with the uint8_t casting to help +// the compiler generate more efficient code. + +void +_lfsr64(uint64_t* io_seed) +{ + *io_seed = (*io_seed << 1) | + S_parity4[(uint8_t)((*io_seed >> 59) & 0x3) | + (uint8_t)((*io_seed >> 60) & 0xc)]; +} diff --git a/src/lib/ppc405lib/lfsr.h b/src/lib/ppc405lib/lfsr.h new file mode 100644 index 0000000..951ec45 --- /dev/null +++ b/src/lib/ppc405lib/lfsr.h @@ -0,0 +1,46 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/lfsr.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __LFSR_H__ +#define __LFSR_H__ + +/// \file lfsr.h +/// \brief Linear-Feedback Shift Register Implementations +/// +/// The 32- and 64-bit pseudo-random number generators in this library are of +/// the linear-conguential type. These maximal-length LFSR pseudo-random +/// sequence generators are also provided. + +/// 64-bit LFSR +/// +/// \param[in,out] io_seed The input seed is converted in one step to the +/// output seed. +/// +/// This 64-bit LFSR uses taps 64, 63, 61, and 60. In big-endian numbering +/// these are bits 0, 1, 3 and 4. This LFSR is also implemented for the PORE +/// engines in the file pore_rand.pS. +void +_lfsr64(uint64_t* io_seed); + +#endif // __LFSR_H__ diff --git a/src/lib/ppc405lib/libppc405files.mk b/src/lib/ppc405lib/libppc405files.mk new file mode 100644 index 0000000..59aa7a0 --- /dev/null +++ b/src/lib/ppc405lib/libppc405files.mk @@ -0,0 +1,74 @@ +# IBM_PROLOG_BEGIN_TAG +# This is an automatically generated prolog. +# +# $Source: src/lib/ppc405lib/libppc405files.mk $ +# +# OpenPOWER OnChipController Project +# +# Contributors Listed Below - COPYRIGHT 2015 +# [+] International Business Machines Corp. +# +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. See the License for the specific language governing +# permissions and limitations under the License. +# +# IBM_PROLOG_END_TAG +# @file libppc405files.mk +# +# @brief mk for libppc405.a object files +# +# @page ChangeLogs Change Logs +# @section libppc405files.mk +# @verbatim +# +# +# Change Log ****************************************************************** +# Flag Defect/Feature User Date Description +# ------ -------------- ---------- ------------ ----------- +# +# @endverbatim +# +########################################################################## +# INCLUDES +########################################################################## + +C-SOURCES = \ + assert.c \ + byte_pool.c \ + ctype.c \ + ctype_table.c \ + fgetc.c \ + initcall.c \ + lfsr.c \ + mutex.c \ + periodic_semaphore.c \ + polling.c \ + printf.c \ + progress.c \ + puts.c \ + rtx_stdio.c \ + simics_stdio.c \ + sprintf.c \ + ssx_dump.c \ + ssx_io.c \ + stdlib.c \ + strcasecmp.c \ + strdup.c \ + string.c \ + string_stream.c \ + strtox.c \ + sxlock.c \ + time.c + +S-SOURCES = + +LIBPPC405_OBJECTS = $(C-SOURCES:.c=.o) $(S-SOURCES:.S=.o) diff --git a/src/lib/ppc405lib/libssx.h b/src/lib/ppc405lib/libssx.h new file mode 100644 index 0000000..601eb00 --- /dev/null +++ b/src/lib/ppc405lib/libssx.h @@ -0,0 +1,44 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/libssx.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __LIBSSX_H__ +#define __LIBSSX_H__ + +// $Id: libssx.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/libssx.h,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file libssx.h +/// \brief Header definitions with no other obvious home + +// Kernel panics + +#define ASSERTION_FAILURE 0x00542701 +#define ERROR_EXIT 0x00542702 + +#endif // __LIBSSX_H__ diff --git a/src/lib/ppc405lib/mutex.c b/src/lib/ppc405lib/mutex.c new file mode 100644 index 0000000..db57f54 --- /dev/null +++ b/src/lib/ppc405lib/mutex.c @@ -0,0 +1,129 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/mutex.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id$ + +/// \file mutex.c +/// \brief A ThreadX-style mutual exclusion object + +#include "mutex.h" + +int +mutex_create(Mutex* i_mutex) +{ + int rc; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(i_mutex == 0, SSX_INVALID_OBJECT); + } + + rc = ssx_semaphore_create(&(i_mutex->sem), 1, 1); + i_mutex->thread = 0; + i_mutex->count = 0; + + return rc; +} + + +// If the current thread owns the Mutex we simply increment the count, +// otherwise pend for the semaphore. +// +// Note: It's possible this doesn't need to be done in a critical section. The +// fact that ssx_semaphore_pend() is atomic may be sufficient since it locks +// the Mutex. + +int +mutex_pend(Mutex* i_mutex, SsxInterval i_timeout) +{ + int rc; + SsxMachineContext ctx; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(i_mutex == 0, SSX_INVALID_OBJECT); + SSX_ERROR_UNLESS_THREAD_CONTEXT(); + } + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + if (i_mutex->thread == ssx_current()) { + + i_mutex->count++; + if (i_mutex->count == 0) { + rc = MUTEX_OVERFLOW; + } else { + rc = 0; + } + + } else { + + rc = ssx_semaphore_pend(&(i_mutex->sem), i_timeout); + if (rc == 0) { + i_mutex->thread = ssx_current(); + i_mutex->count = 1; + } + } + + ssx_critical_section_exit(&ctx); + + return rc; +} + + +// If the current thread owns the Mutex we decrement the count and free the +// object when the count goes to 0. +// +// Note: It's possible this doesn't need to be done in a critical section. The +// fact that ssx_semaphore_pend() is atomic may be sufficient since it locks +// the Mutex. + +int +mutex_post(Mutex* i_mutex) +{ + int rc; + SsxMachineContext ctx; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(i_mutex == 0, SSX_INVALID_OBJECT); + SSX_ERROR_UNLESS_THREAD_CONTEXT(); + } + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + SSX_ERROR_IF(i_mutex->thread != ssx_current(), MUTEX_NOT_OWNED); + + if (--i_mutex->count == 0) { + i_mutex->thread = 0; + rc = ssx_semaphore_post(&(i_mutex->sem)); + } else { + rc = 0; + } + + ssx_critical_section_exit(&ctx); + + return rc; +} + + + + diff --git a/src/lib/ppc405lib/mutex.h b/src/lib/ppc405lib/mutex.h new file mode 100644 index 0000000..6d3a352 --- /dev/null +++ b/src/lib/ppc405lib/mutex.h @@ -0,0 +1,164 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/mutex.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __MUTEX_H__ +#define __MUTEX_H__ + +// $Id$ + +/// \file mutex.h +/// \brief A ThreadX-style mutual exclusion object +/// +/// A Mutex is a binary semaphore with the concept of thread ownership. A +/// thread first obtains the Mutex using the mutex_pend() API, which may block +/// if the Mutex is currently owned by another thread. Once a thread owns a +/// Mutex, subsequent calls of mutex_pend() by the same thread simply +/// increment an internal counter, but do not block. Once a thread has +/// executed a matching mutex_post() call for every mutex_pend() call, the +/// Mutex is free for another thread. +/// +/// This type of mutual exclusion object is useful for example to control +/// access to data structures that are manipulated by APIs with several common +/// entry points. Each call of an API in the chain will 'lock' the data +/// structure using mutex_pend()/mutex_post(). The Mutex semantics allows +/// multiple "locks" by the same thread, but requires a corresponding "unlock" +/// for every "lock". +/// +/// The Mutex usage counter is a 32-bit unsigned integer. If a thread makes +/// 2^32 calls to mutex_pend() without an intervening call of mutex_post(), an +/// overflow is signalled. This error should be considered unrecoverable to +/// the application. +/// +/// Like the SSX semaphore, no record is kept in the thread of which Mutex +/// objects are currently owned by the thread. If a thread terminates or is +/// deleted while holding a Mutex it is likely that the application will +/// hang. Unlike the SSX semaphore, it is absolutely illegal to call +/// mutex_pend() and mutex_post() from interrupt contexts. It is also illegal +/// for a thread to call mutex_post() for a mutex it does not own. +/// +/// Mutex objects are easily created with the static initialization macro +/// MUTEX_INITIALIZATION as in the following example. +/// +/// \code +/// +/// Mutex G_mutex = MUTEX_INITIALIZATION; +/// +/// \endcode +/// +/// The API mutex_create() is also provided for run-time initialization. + +#include "ssx.h" + + +// Mutex error/panic codes + +#define MUTEX_OVERFLOW 0x00688901 +#define MUTEX_NOT_OWNED 0x00688902 + + +#ifndef __ASSEMBLER__ + +/// Static initialization of a Mutex +/// +/// For a full description of the Mutex please see the documentation fof the +/// file mutex.h. +#define MUTEX_INITIALIZATION {SSX_SEMAPHORE_INITIALIZATION(1, 1), 0, 0} + + +/// The Mutex object + +typedef struct { + + /// The binary semaphore + SsxSemaphore sem; + + /// A pointer to the owning thread, or NULL (0) + SsxThread* thread; + + /// The count of unmatched mutex_pend() calls made by the owning thread. + uint32_t count; + +} Mutex; + + +/// Create (initialize) a Mutex +/// +/// \param[in] i_mutex A pointer to the Mutex object to initialize. +/// +/// For a full description of the Mutex please see the documentation for the +/// file mutex.h. +/// +/// \retval 0 Success +/// +/// \retval 0 -SSX_INVALID_OBJECT The \a i_mutex is NULL (0). +int +mutex_create(Mutex* i_mutex); + + +/// Pend on a Mutex with optional timeout +/// +/// \param[in] i_mutex A pointer to the Mutex +/// +/// \param[in] i_timeout Either the constant SSX_WAIT_FOREVER, or a timeout +/// interval specification. +/// +/// For a full description of the Mutex please see the documentation for the +/// file mutex.h. +/// +/// \retval 0 Success +/// +/// \retval -SSX_INVALID_OBJECT The \a i_mutex is NULL (0). +/// +/// \retval -SSX_ILLEGAL_CONTEXT The call was not made from a thread context. +/// +/// \retval -SSX_SEMEPHORE_PEND_TIMED_OUT The thread was not able to obtain +/// the Mutex before the timeout. +/// +/// \retval -MUTEX_OVERFLOW The owning thread has made 2^32 unmatched calls of +/// mutex_pend(). +int +mutex_pend(Mutex* i_mutex, SsxInterval i_timeout); + + +/// Post to a Mutex +/// +/// \param[in] i_mutex A pointer to the Mutex +/// +/// For a full description of the Mutex please see the documentation for the +/// file mutex.h. +/// +/// \retval 0 Success +/// +/// \retval -SSX_INVALID_OBJECT The \a i_mutex is NULL (0). +/// +/// \retval -SSX_ILLEGAL_CONTEXT The call was not made from a thread context. +/// +/// \retval -MUTEX_NOT_OWNED The thread calling mutex_post() does not own the +/// Mutex. +int +mutex_post(Mutex* i_mutex); + +#endif // __ASSEMBLER__ + +#endif // __MUTEX_H__ diff --git a/src/lib/ppc405lib/periodic_semaphore.c b/src/lib/ppc405lib/periodic_semaphore.c new file mode 100644 index 0000000..dec9ea2 --- /dev/null +++ b/src/lib/ppc405lib/periodic_semaphore.c @@ -0,0 +1,114 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/periodic_semaphore.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id$ + +/// \file perodic_semaphore.h +/// \brief Periodic semphores + +#include "ssx.h" +#include "periodic_semaphore.h" + +// The timer callback is created nonpreemptible, so noncritical interrupts are +// disabled at entry. + +static void +_periodic_semaphore_timeout(void* arg) +{ + PeriodicSemaphore* ps; + + ps = (PeriodicSemaphore*)arg; + + if (ps->sem.count != 1) { + ssx_semaphore_post(&(ps->sem)); + } +} + + +int +periodic_semaphore_create(PeriodicSemaphore* sem, SsxInterval period) +{ + int rc; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(sem == 0, SSX_INVALID_OBJECT); + } + + do { + rc = ssx_semaphore_create(&(sem->sem), 0, 1); + if (rc) break; + + rc = ssx_timer_create_nonpreemptible(&(sem->timer), + _periodic_semaphore_timeout, + sem); + if (rc) break; + + rc = ssx_timer_schedule(&(sem->timer), + period, + period); + if (rc) break; + + } while (0); + + return rc; +} + + +int +periodic_semaphore_pend(PeriodicSemaphore* sem) +{ + int rc; + SsxMachineContext ctx; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(sem == 0, SSX_INVALID_OBJECT); + } + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + if (sem->sem.count == 0) { + + rc = ssx_semaphore_pend(&(sem->sem), SSX_WAIT_FOREVER); + + } else { + + sem->sem.count = 0; + rc = -PERIODIC_SEMAPHORE_OVERRUN; + } + + ssx_critical_section_exit(&ctx); + + return rc; +} + + +int +periodic_semaphore_cancel(PeriodicSemaphore* sem) +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(sem == 0, SSX_INVALID_OBJECT); + } + + return ssx_timer_cancel(&(sem->timer)); +} diff --git a/src/lib/ppc405lib/periodic_semaphore.h b/src/lib/ppc405lib/periodic_semaphore.h new file mode 100644 index 0000000..c3c2e53 --- /dev/null +++ b/src/lib/ppc405lib/periodic_semaphore.h @@ -0,0 +1,152 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/periodic_semaphore.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PERIODIC_SEMAPHORE_H__ +#define __PERIODIC_SEMAPHORE_H__ + +// $Id$ + +/// \file perodic_semaphore.h +/// \brief Periodic semphores +/// +/// The PeriodicSemphore is a simple abstraction introduced to simplify coding +/// peridic threads. A periodic thread creates the PeriodicSemaphore after +/// thread initialization, but prior to the entry to the periodic infinite +/// loop. This creates the periodicSemaphore with a count of 0. Once thread +/// processing is finished, the thread pends on the PeriodicSemaphore. A +/// periodic timer posts to the PeriodicSemaphore on a fixed, absolute period +/// to reschedule the thread. +/// +/// If the thread pends on the PeriodicSemaphore and the timer has already +/// posted to the semaphore, the call of periodic_semaphore_pend() clears the +/// semaphore and terminates immediately with the return code +/// -PERIODIC_SEMAPHORE_OVERRUN, indicating that the thread has overrun its +/// period. The thread can choose the appropriate action upon obtaining this +/// return code. +/// +/// The PeriodicSemaphore can also be cancelled, which simply cancels the +/// periodic timer posting to the semaphore. If the thread needs to +/// re-initialize the PeriodicSemaphore for any reason (e.g, to resynchronize +/// after an overrun) it must be cancelled first. + +// Error/Panic codes + +#define PERIODIC_SEMAPHORE_OVERRUN 0x0077e601 + + +/// A periodic semaphore + +typedef struct { + + /// The semaphore + SsxSemaphore sem; + + /// The timer + SsxTimer timer; + +} PeriodicSemaphore; + + +/// Create (initialize) a PeriodicSemaphore +/// +/// \param sem A pointer to an uninitialized or inactive +/// PeriodicSemaphore. +/// +/// \param period The semaphore period +/// +/// This API creates the embedded semaphore as a binary semaphore with an +/// initial value of 0, and schedules a periodic timer to post to the +/// semaphore. +/// +/// \retval 0 Success +/// +/// \retval -SSX_INVALID_OBJECT The \a sem was NULL (0) or otherwise invalid. +/// +/// Other return codes are possible from the embedded calls of SSX APIs. +int +periodic_semaphore_create(PeriodicSemaphore* sem, SsxInterval period); + + +/// Pend on a PeriodicSemaphore +/// +/// \param sem A pointer to an initialized PeriodicSemaphore +/// +/// Pend on a PeriodicSemaphore. It is considered a non-fatal error if the +/// semaphore has a non-0 count as this may indicate that a periodic thread +/// has missed a deadline. +/// +/// Return values other than SSX_OK (0) are not necessarily errors; see \ref +/// ssx_errors +/// +/// The following return codes are non-error codes: +/// +/// \retval 0 Success. In particular, the semaphore count was 0 at entry. +/// +/// \retval -PERIODIC_SEMAPHORE_OVERRUN This return code indicates that the +/// semaphore count was 1 at entry. This code is always returned (never causes +/// a panic). +/// +/// The following return codes are error codes: +/// +/// \retval -SSX_IVALID_OBJECT The \a sem was NULL () or otherwise invalid at +/// entry. +/// +/// Other error return codes are possible from embedded calls of SSX APIs. +int +periodic_semaphore_pend(PeriodicSemaphore* sem); + + +/// Cancel a periodic semaphore +/// +/// \param sem A pointer to an initialized PeriodicSemaphore +/// +/// Cancel the PeriodicSemaphore timeout. This is a required step if the +/// PeriodicSemaphore is to be reinitialized. This is also required if the +/// PeriodicSemaphore is created on the thread stack and the thread +/// terminates. PeriodicSemaphore can be canceled at any time. It is never +/// an error to call periodic_semaphore_cancel() on a PeriodicSemaphore object +/// after it is created. +/// +/// Return values other than SSX_OK (0) are not necessarily errors; see \ref +/// ssx_errors +/// +/// The following return codes are non-error codes: +/// +/// \retval 0 Successful completion +/// +/// \retval -SSX_TIMER_NOT_ACTIVE The embedded timer is not currently +/// scheduled, meaning that the PeriodicSemaphore was previosly +/// cancelled. +/// +/// The following return codes are error codes: +/// +/// \retval -SSX_IVALID_OBJECT The \a sem was NULL () or otherwise invalid at +/// entry. +/// +/// Other error return codes are possible from embedded calls of SSX APIs. +int +periodic_semaphore_cancel(PeriodicSemaphore* sem); + + +#endif // __PERIODIC_SEMAPHORE_H__ diff --git a/src/lib/ppc405lib/polling.c b/src/lib/ppc405lib/polling.c new file mode 100644 index 0000000..a013202 --- /dev/null +++ b/src/lib/ppc405lib/polling.c @@ -0,0 +1,97 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/polling.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id: polling.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/polling.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file polling.c +/// \brief Library APIs for polling + +#include "polling.h" + +int +polling(int* o_rc, + int (*i_condition)(void* io_arg, int* o_satisfied), + void* io_arg, + SsxInterval i_timeout, + SsxInterval i_sleep) +{ + SsxTimebase start; + int rc, pollRc, timed_out, done; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF((i_condition == 0), POLLING_ERROR); + } + + start = ssx_timebase_get(); + timed_out = 0; + + do { + pollRc = i_condition(io_arg, &done); + if (pollRc) { + rc = POLLING_CONDITION; + break; + } + if (done) { + rc = 0; + break; + } + if (timed_out) { + rc = POLLING_TIMEOUT; + break; + } + if (i_sleep != 0) { + rc = ssx_sleep(i_sleep); + if (rc) { + break; + } + } + timed_out = + ((i_timeout != SSX_WAIT_FOREVER) && + ((ssx_timebase_get() - start) >= i_timeout)); + + } while (1); + + if (o_rc) { + *o_rc = pollRc; + } + + return rc; +} + + +void +busy_wait(SsxInterval i_interval) +{ + SsxTimebase start; + + start = ssx_timebase_get(); + while ((ssx_timebase_get() - start) < i_interval); +} + diff --git a/src/lib/ppc405lib/polling.h b/src/lib/ppc405lib/polling.h new file mode 100644 index 0000000..99afbe7 --- /dev/null +++ b/src/lib/ppc405lib/polling.h @@ -0,0 +1,118 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/polling.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __POLLING_H__ +#define __POLLING_H__ + +// $Id: polling.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/polling.h,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file polling.c +/// \brief Library APIs for polling and busy-waiting + +#include "ssx.h" + +// Return/Panic codes + +#define POLLING_TIMEOUT 0x00765501 +#define POLLING_ERROR 0x00765502 +#define POLLING_CONDITION 0x00765503 + + +#ifndef __ASSEMBLER__ + +/// Poll for a condition or a timeout with optional sleep +/// +/// \param[out] o_rc The last return code from calling \a i_condition. This +/// will only be valid if the return code from polling() is +/// POLLING_CONDITION. This argument may be passed as NULL (0) if the caller +/// does not require this information. +/// +/// \param[in] i_condition A function of two arguments, returning an integer +/// return code - 0 for success, non-0 for failure. The first argument is a +/// private state or parameter variable. The second argument is used to +/// return the truth value of the \a i_condition predicate (0 for false, non-0 +/// for true), and is only considered if the return value of \a i_condition is +/// 0. +/// +/// \param[in,out] io_arg The private argument of the \a condition function. +/// +/// \param[in] i_timeout The maximum amount of time to poll the \a condition +/// before declaring a timeout. The special value SSX_WAIT_FOREVER can be +/// used to specify polling without timeout. +/// +/// \param[in] i_sleep If non-0 at entry, then the thread will sleep for this +/// interval between polls of the condition. Otherwise the polling is +/// continuous. polling() can only be called with i_sleep non-0 from a +/// thread context (since interrupt contexts can not block). +/// +/// polling() implements a generic polling protocol for conditions that can +/// not be recognized as interrupt events. polling() polls the \a i_condition +/// until either an error is encountered, the condition is true, or the +/// polling times out as measured by the SSX timebase. Whenever a timeout is +/// detected the condition is polled once more to exclude false timeouts that +/// may have been caused by thread preemption. +/// +/// The \a i_sleep A non-0 value of \a i_sleep specifies that the thread +/// should sleep for the given interval between polling tries instead of +/// polling continuously. A non-0 \a i_sleep argument is only legal in thread +/// contexts. +/// +/// \retval 0 Success; The condition was satisfied prior to the timeout. +/// +/// \retval POLLING_TIMEOUT A timeout was detected before the condition became +/// valid. +/// +/// \retval POLLING_ERROR This code is returned if any of the arguments of +/// polling() are invalid. +/// +/// \retval POLLING_CONDITION This code is returned if the \a i_condition +/// function returns a non-0 return code. +/// +/// If the embedded call of ssx_sleep() fails for some reason then the return +/// code will be the code returned by ssx_sleep(). +int +polling(int* o_rc, + int (*i_condition)(void* io_arg, int* o_satisfied), + void* io_arg, + SsxInterval i_timeout, + SsxInterval i_sleep); + + +/// A busy-wait loop +/// +/// \param[in] i_interval The interval of time to busy-wait. The actual +/// interval may be more than this if the thread is interrupted. If called +/// from a context with interrupts disabled the timing should be very precise. +void +busy_wait(SsxInterval i_interval); + +#endif // __ASSEMBLER__ + +#endif // __POLLING_H__ diff --git a/src/lib/ppc405lib/printf.c b/src/lib/ppc405lib/printf.c new file mode 100644 index 0000000..d75cdf3 --- /dev/null +++ b/src/lib/ppc405lib/printf.c @@ -0,0 +1,703 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/printf.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id: printf.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/printf.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file printf.c +/// \brief Clean-room implementation of printf() functions for SSX I/O +/// +/// For licensing reasons we are required to create our own version of the +/// printf() family of functions. This implementation was created without +/// reference to or inclusion of any licensed or copyrighted code. +/// +/// The functions defined in this file have prototypes, behavior and return +/// values as defined by C language standards. In the event of an error a +/// negative value is returned, generally corresponding to a standard Unix +/// 'errno' code. Note that SSX does not support either an application- or +/// per-thread 'errno', so the only record of any error is the \a error field +/// of the stream. Also note that SSX may be configured to cause a panic if an +/// error is detected rather than returning an error code. +/// +/// This implementation defines a limited but useful subset of the C standard +/// for format control. This implementation includes the following: +/// +/// - \b c, \b d, \b i, \b n, \b p, \b s, \b u, \b x, and \b X conversion +/// specifiers, as well as '%%' to output a single '%' +/// +/// - \b #, \b 0, \b ' ' and \b + flag characters +/// +/// - Decimal field width specifiers including * (but indirect field widths +/// must be positive as left-justification is not supported) +/// +/// - Decimal precision specifiers (currently only apply to %s formats, may be +/// indirect using *) +/// +/// - \b l, \b ll and \b z length modifiers +/// +/// \b Notes: +/// +/// \a If a \c p conversion specifier is used without any flags (\c '%p'), the +/// \c p conversion is interptered as if it were \c 0x%08lx for 32-bit address +/// machines and \c 0x%016llx for 64-bit address machines. The GCC builtin +/// format checker gives warnings about '0' flag characters for \c p +/// conversion specifiers, so there is otherwise no 'un-warned' way to get +/// this preferred (by some) format of pointer values. If you do include +/// explicit flags (e.g., \c %30p) they will be processed as expected. +/// +/// Similar to how printf() behaves on an X86-Linux machine, a null pointer +/// will print as "(null)" with the %s format (unless the precision specifier +/// precludes it) and "(nil)" with the %p format. +/// +/// Note that calling formatted I/O functions on non-blocking streams may fail +/// with the -EAGAIN error, and there is no clean way to restart these +/// calls. Calling formatted (or any) I/O functions on blocking streams from +/// interrupt contexts in SSX is also likely to fail intermittently since +/// interrupt contexts can not block in SSX. +/// +/// \todo I'd really like to implement the '-' flag for +/// left-justification. Implementing the precision specifer for integers +/// should be done for completeness. + +#include "ssx.h" +#include "ssx_io.h" + +// Formatting options + +#define OPTION_ALTERNATE 0x0001 +#define OPTION_PAD_ZERO 0x0002 +#define OPTION_PLUS_SIGN 0x0004 +#define OPTION_FIELD_WIDTH 0x0008 +#define OPTION_PRECISION 0x0010 +#define OPTION_LONG 0x0020 +#define OPTION_LONG_LONG 0x0040 +#define OPTION_SIZE_T 0x0080 +#define OPTION_UPPERCASE 0x0100 +#define OPTION_HEX 0x0200 +#define OPTION_SPACE 0x0400 + + +// Generate padding if required, returning the total number of pad characters +// output or a negative error code. The 'nchars' argument is the number of +// non-pad characters to be output by the caller. + +#define PAD_SIZE 8 +static const char zeros[PAD_SIZE] = {'0', '0', '0', '0', '0', '0', '0', '0'}; +static const char blanks[PAD_SIZE] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}; + +static ssize_t +pad(FILE *stream, size_t nchars, int options, size_t width) +{ + const char *padchars; + size_t chars, written; + int rc; + + if (!(options & OPTION_FIELD_WIDTH) || (nchars >= width)) { + return 0; + } + chars = width - nchars; + if (options & OPTION_PAD_ZERO) { + padchars = zeros; + } else { + padchars = blanks; + } + while (chars) { + rc = swrite(stream, (void *)padchars, MIN(chars, PAD_SIZE), &written); + if (rc < 0) return rc; + chars -= written; + } + return width - nchars; +} + + +// Format a character + +static ssize_t +format_char(FILE *stream, unsigned char c, int options, size_t width) +{ + ssize_t padchars, nchars; + int rc; + + padchars = pad(stream, 1, options, width); + if (padchars < 0) return padchars; + nchars = padchars + 1; + rc = swrite(stream, (void *)(&c), 1, 0); + if (rc < 0) return rc; + return nchars; +} + + +// Format a string +// +// If the string is the NULL pointer then normally "(null)" is printed +// unless the precision is < 6, in which case the empty string is printed. +// The specification leaves it as undefined what happens if a string requests +// 0 padding; Here we always pad with blanks (although GCC/PowerPC catches +// this as an error). + +static ssize_t +format_string(FILE *stream, const char *s, int options, + size_t width, size_t precision) +{ + size_t len; + ssize_t padchars, nchars; + int rc; + + if (s == 0) { + if ((options & OPTION_PRECISION) && (precision < 6)) { + s = ""; + } else { + s = "(null)"; + } + } + + len = strlen(s); + if (options & OPTION_PRECISION) { + len = MIN(len, precision); + } + + options &= ~OPTION_PAD_ZERO; + padchars = pad(stream, len, options, width); + if (padchars < 0) return padchars; + nchars = padchars + len; + rc = swrite(stream, (void *)s, len, 0); + if (rc < 0) return rc; + return nchars; +} + + +// Format an integer - signed and unsigned. A 64-bit integer (assumed to be +// the longest we'll see) has 20 decimal digits. An extra space is reserved +// for the sign. If zero-padding is specified, the sign will be output +// separately. + +static const char lower[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + +static const char upper[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + +static ssize_t +format_int(FILE *stream, long long int lli, int options, size_t width) +{ + char digits[21]; + int rc, k, ndigits, negative, positive, i; + ssize_t output; + + negative = (lli < 0); + positive = (lli > 0); + + // Unpack the integer to characters. The code is optimized for 32-bit + // machines where 64-bit division is not built in. The first part of the + // loop handles integers requiring a 64-bit divide, the second loop + // handles 32-bit integers. + + if (lli == 0) { + digits[20] = '0'; + k = 20; + } else if (negative) { + for (k = 21; + lli != (int)lli; + digits[--k] = lower[-(lli % 10)], lli = lli / 10); + for (i = (int)lli; + i != 0; + digits[--k] = lower[-(i % 10)], i = i / 10); + } else { + for (k = 21; + lli != (int)lli; + digits[--k] = lower[lli % 10], lli = lli / 10); + for (i = (int)lli; + i != 0; + digits[--k] = lower[i % 10], i = i / 10); + } + + ndigits = 21 - k; + + // Handle other options and output + + output = 0; + if (options & OPTION_PAD_ZERO) { + + if (negative) { + rc = swrite(stream, "-", 1, 0); + if (rc < 0) return rc; + output++; + } else if (positive) { + if (options & OPTION_PLUS_SIGN) { + rc = swrite(stream, "+", 1, 0); + if (rc < 0) return rc; + output++; + } else if (options & OPTION_SPACE) { + rc = swrite(stream, " ", 1, 0); + if (rc < 0) return rc; + output++; + } + } + rc = pad(stream, ndigits + output, options, width); + if (rc < 0) return rc; + output += rc; + rc = swrite(stream, &(digits[k]), ndigits, 0); + if (rc < 0) return rc; + output += ndigits; + + } else { + + if (negative) { + digits[--k] = '-'; + ndigits++; + } else if (positive) { + if (options & OPTION_PLUS_SIGN) { + digits[--k] = '+'; + ndigits++; + } else if (options & OPTION_SPACE) { + digits[--k] = ' '; + ndigits++; + } + } + rc = pad(stream, ndigits, options, width); + if (rc < 0) return rc; + output += rc; + rc = swrite(stream, &(digits[k]), ndigits, 0); + if (rc < 0) return rc; + output += ndigits; + } + + return output; +} + + +static ssize_t +format_unsigned(FILE *stream, unsigned long long ull, int options, size_t width) +{ + char digits[21], *alternate; + const char *xchars; + int rc, k, ndigits, zero; + unsigned u; + ssize_t output; + + zero = (ull == 0); + + // Determine hex case and alternate string + + alternate = 0; + if (options & OPTION_HEX) { + if (options & OPTION_UPPERCASE) { + xchars = upper; + if (options & OPTION_ALTERNATE) { + alternate = "0X"; + } + } else { + xchars = lower; + if (options & OPTION_ALTERNATE) { + alternate = "0x"; + } + } + } else { + xchars = lower; + } + + // Unpack the unsigned integer to characters. The Hex conversions are + // easier since they can be done with shift and mask rather than + // divison. The code is optimized for a 32-bit machine where 64-bit + // division is not built-in. + + if (zero) { + digits[20] = '0'; + k = 20; + } else if (options & OPTION_HEX) { + for (k = 21; + ull != (unsigned)ull; + digits[--k] = xchars[ull & 0xf], ull = ull >> 4); + for (u = (unsigned)ull; + u != 0; + digits[--k] = xchars[u & 0xf], u = u >> 4); + } else { + for (k = 21; + ull != (unsigned)ull; + digits[--k] = xchars[ull % 10], ull = ull / 10); + for (u = (unsigned)ull; + u != 0; + digits[--k] = xchars[u % 10], u = u / 10); + } + + ndigits = 21 - k; + + // Handle other options and output + + output = 0; + if (options & OPTION_PAD_ZERO) { + + if (!zero && alternate) { + rc = swrite(stream, (void *)alternate, 2, 0); + if (rc < 0) return rc; + output += 2; + } + rc = pad(stream, ndigits + output, options, width); + if (rc < 0) return rc; + output += rc; + rc = swrite(stream, &(digits[k]), ndigits, 0); + if (rc < 0) return rc; + output += ndigits; + + } else { + + if (!zero && alternate) { + output += 2; + } + rc = pad(stream, ndigits + output, options, width); + if (rc < 0) return rc; + output += rc; + if (!zero && alternate) { + rc = swrite(stream, alternate, 2, 0); + if (rc < 0) return rc; + output += 2; + } + rc = swrite(stream, &(digits[k]), ndigits, 0); + if (rc < 0) return rc; + output += ndigits; + } + + return output; +} + + +int +vfprintf(FILE *stream, const char *format, va_list argp) +{ + const char *fmt, *scan; + int rc, total_chars, options, done; + size_t width, precision; + + int arg_i, *arg_pi; + long int arg_li; + long long int arg_lli; + ssize_t arg_zi; + unsigned arg_u; + unsigned long arg_lu; + unsigned long long arg_llu; + size_t arg_zu; + char *arg_s; + + total_chars = 0; + + fmt = format; + while (*fmt) { + + // Scan until '%' or the end of the format, then output the text. + + scan = fmt; + while (*scan && (*scan != '%')) { + scan++; + } + if (scan != fmt) { + rc = swrite(stream, fmt, scan - fmt, 0); + if (rc < 0) return rc; + total_chars += scan - fmt; + } + fmt = scan; + if (!*fmt) { + return total_chars; + } + fmt++; + + // We got a '%'. Check for %% and %n. + + switch (*fmt) { + case '\0': + SSX_IO_ERROR(stream, EINVAL); + break; + case '%': + rc = swrite(stream, "%", 1, 0); + if (rc < 0) return rc; + total_chars++; + fmt++; + continue; + case 'n': + arg_pi = va_arg(argp, int *); + *arg_pi = total_chars; + fmt++; + continue; + } + + // Collect padding options, if any. Left justification is not + // implemeted. + + options = 0; + done = 0; + do { + switch (*fmt) { + case '\0': + SSX_IO_ERROR(stream, EINVAL); + break; + case '#': + options |= OPTION_ALTERNATE; + break; + case '0': + options |= OPTION_PAD_ZERO; + break; + case '+': + options |= OPTION_PLUS_SIGN; + break; + case ' ': + options |= OPTION_SPACE; + break; + case '-': + SSX_IO_ERROR(stream, EINVAL); // Left just. not impl. + break; + default: + done = 1; + break; + } + if (!done) { + fmt++; + } + } while (!done); + + // Collect the field width, if specified. A negative precision + // specified as an argument indicates left justification (not + // implemented). + + width = 0; + if (isdigit(*fmt)) { + options |= OPTION_FIELD_WIDTH; + for (; isdigit(*fmt); fmt++) { + width = (width * 10) + (*fmt - '0'); + } + } else if (*fmt == '*') { + fmt++; + options |= OPTION_FIELD_WIDTH; + arg_i = va_arg(argp, int); + if (arg_i < 0) { + SSX_IO_ERROR(stream, EINVAL); // Left just. not impl. + } + width = arg_i; + } + + // Collect the precision, if specified. By standard specification an + // empty or negative precision is interpreted as 0. + + precision = 0; + if (*fmt == '.') { + fmt++; + options |= OPTION_PRECISION; + if (isdigit(*fmt)) { + for(; isdigit(*fmt); fmt++) { + precision = (precision * 10) + (*fmt - '0'); + } + } else if (*fmt == '*') { + fmt++; + arg_i = va_arg(argp, int); + if (arg_i < 0) { + arg_i = 0; + } + precision = arg_i; + } + } + + // Collect length modifiers. + + done = 0; + do { + switch (*fmt) { + case '\0': + SSX_IO_ERROR(stream, EINVAL); + break; + case 'l': + if (options & OPTION_LONG) { + options &= ~OPTION_LONG; + options |= OPTION_LONG_LONG; + } else if (options & OPTION_LONG_LONG) { + SSX_IO_ERROR(stream, EINVAL); + } else { + options |= OPTION_LONG; + } + if (options & OPTION_SIZE_T) { + SSX_IO_ERROR(stream, EINVAL); + } + break; + case 'z': + if ((options & OPTION_LONG) || (options & OPTION_LONG_LONG)) { + SSX_IO_ERROR(stream, EINVAL); + } + options |= OPTION_SIZE_T; + break; + default: + done = 1; + break; + } + if (!done) { + fmt++; + } + } while (!done); + + // Use the conversion specifier to format the next argument + + switch (*fmt) { + + case 'c': + arg_i = va_arg(argp, int); + rc = format_char(stream, (unsigned char)arg_i, options, width); + if (rc < 0) return rc; + total_chars++; + break; + + case 'd': + case 'i': + if (options & OPTION_LONG) { + arg_li = va_arg(argp, long int); + rc = format_int(stream, (long long int)arg_li, options, + width); + } else if (options & OPTION_LONG_LONG) { + arg_lli = va_arg(argp, long long int); + rc = format_int(stream, (long long int)arg_lli, options, + width); + } else if (options & OPTION_SIZE_T) { + arg_zi = va_arg(argp, ssize_t); + rc = format_int(stream, (long long int)arg_zi, options, + width); + } else { + arg_i = va_arg(argp, int); + rc = format_int(stream, (long long int)arg_i, options, + width); + } + if (rc < 0) return rc; + total_chars += rc; + break; + + case 'p': + arg_lu = va_arg(argp, unsigned long); + options |= (OPTION_ALTERNATE | OPTION_HEX); + if (!(options & OPTION_PAD_ZERO) && + !(options & OPTION_FIELD_WIDTH)) { + options |= (OPTION_PAD_ZERO | OPTION_FIELD_WIDTH); + width = (2 * sizeof(unsigned long)) + 2; /* 0x........ */ + } + if (arg_lu == 0) { + options &= ~OPTION_PRECISION; + rc = format_string(stream, "(nil)", options, width, precision); + } else { + rc = format_unsigned(stream, (unsigned long long)arg_lu, + options, width); + } + if (rc < 0) return rc; + total_chars += rc; + break; + + case 's': + arg_s = va_arg(argp, char *); + rc = format_string(stream, arg_s, options, width, precision); + if (rc < 0) return rc; + total_chars += rc; + break; + + case 'X': + options |= OPTION_UPPERCASE; + case 'x': + options |= OPTION_HEX; + case 'u': + if (options & OPTION_LONG) { + arg_lu = va_arg(argp, unsigned long); + rc = format_unsigned(stream, (unsigned long long)arg_lu, + options, width); + } else if (options & OPTION_LONG_LONG) { + arg_llu = va_arg(argp, unsigned long long); + rc = format_unsigned(stream, (unsigned long long)arg_llu, + options, width); + } else if (options & OPTION_SIZE_T) { + arg_zu = va_arg(argp, size_t); + rc = format_unsigned(stream, (unsigned long long)arg_zu, + options, width); + } else { + arg_u = va_arg(argp, unsigned); + rc = format_unsigned(stream, (unsigned long long )arg_u, + options, width); + } + if (rc < 0) return rc; + total_chars += rc; + break; + + default: + SSX_IO_ERROR(stream, EINVAL); + break; + } + + fmt++; + } + + return total_chars; +} + + +int +vprintf(const char *format, va_list argp) +{ + return vfprintf(stdout, format, argp); +} + + +int +fprintf(FILE *stream, const char *format, ...) +{ + va_list argp; + int rc; + + va_start(argp, format); + rc = vfprintf(stream, format, argp); + va_end(argp); + return rc; +} + + +int +printf(const char *format, ...) +{ + va_list argp; + int rc; + + va_start(argp, format); + rc = vfprintf(stdout, format, argp); + va_end(argp); + return rc; +} + + +int +printk(const char *format, ...) +{ + va_list argp; + int rc; + + va_start(argp, format); + rc = vfprintf(ssxout, format, argp); + va_end(argp); + return rc; +} + diff --git a/src/lib/ppc405lib/progress.c b/src/lib/ppc405lib/progress.c new file mode 100644 index 0000000..8d2cd30 --- /dev/null +++ b/src/lib/ppc405lib/progress.c @@ -0,0 +1,743 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/progress.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id + +/// \file progress.c +/// \brief Programmable progress (hang) checking +/// +/// This is a simple implementation of a progress (hang) checking +/// facility. The application provides an array of \e pass \e counts that are +/// expected to update/count over time. For simplicity and generality all +/// pass counts are defined to be \c uint64_t types. The +/// progress_checker_create() API initializes the checker, and as a +/// convenience clears the pass counts. +/// +/// The checker can be created with an optional \a callback function, which +/// has the prototype +/// +/// \code +/// typedef int (*ProgressCheckerCallback)(struct ProgressChecker *checker, +/// void* arg, +/// size_t failed) +/// \endcode +/// +/// The checker callback is called \e every time a check is made by +/// progress_checker_check(). In addition to a private void* argument, the +/// parameter list of the callback includes a count of the number of counters +/// that failed to make progress - this count will be 0 if the check was +/// successful. The return value of the callback is passed back as the return +/// value of progress_checker_check(). If the callback is specified as NULL +/// (0), then a successful check returns 0, and any failure causes a return +/// code of -PROGRESS_CHECKER_FAILED. +/// +/// The application can dynamically mark counters as either \e exempt or \e +/// required. By default all counts are required to have increased +/// each time a check is made for progress. Counters marked \e exempt when a +/// progress check is made are not checked for progress. +/// +/// The application can also use the progress_checker_schedule() API to +/// schedule either one-shot or periodic checks. The +/// progress_checker_cancel() API can be used to cancel any scheduled +/// checks. It is never an error to call this API, even if no checks are +/// currently scheduled. Note that each call of progress_checker_schedule() +/// also cancels any outstanding scheduled requests before (re-) scheduling +/// the checker. If using the built-in timer mechanism, any calls of +/// progress_checker_check that return a non-0 value will cause a kernel panic. +/// +/// If failures are detected and caught, the ProgressChecker provides a +/// primitive iteration facility for the callback or the applicaton to +/// determine which counters have failed to update. Calling +/// progress_checker_next_failure() returns either the index of the next +/// failing counter, or -1 to indicate no more failures. This iteration +/// facility is reset every time a check is made by progress_checker_check() +/// (including those made implcitly by the timer-based mechanism). There is no +/// API to reset the iteration. +/// +/// The implemetation provides 2 standard callback functions: +/// progess_checker_printk() and progress_checker_printk_dump(). The former +/// callback uses printk() to print a simple report of failed counters, and if +/// there were any failures it then returns its argument as a return code. If +/// the return code is non-zero then the lack of progress will cause a kernel +/// panic (test failure). The later callback first calls +/// progress_checker_printk(). If progress_checker_printk() returns a non-0 +/// value then progress_checker_printk_dump() enters an SSX_CRITICAL crictal +/// section and prints a full kernel state dump that may be useful to help +/// diagnose the hang. +/// +/// \note We do not make the kernel dump the default or only behavior because +/// it could take 1ms or more to produce the large quantity of formatted +/// output required, which could be a significant amount of wall time in a +/// logic simulation environment. +/// +/// The progress_checker_create() API could be used in a couple of ways as +/// illustrated below: +/// +/// \code +/// +/// ProgressChecker progress; +/// uint64_t counter; +/// +/// progress_checker_create(&progress, "progress", counter, 1, +/// progress_checker_printk, +/// (void*)-PROGRESS_CHECKER_FAILED); +/// +/// OR +/// +/// progress_checker_create(&progress, "progress", counter, 1, +/// progress_checker_printk, 0); +/// +/// \endcode +/// +/// The first usage prints a report and panics the test if lack of progress is +/// detected. The second form simply prints a report in the event of a lack +/// or progress. The second form may be useful to report on counters that +/// only have a statistical probability of making progress, however be aware +/// that the report is generated in an interrupt context and all thread +/// activity will be blocked until the formatted I/O is complete. +/// +/// Notes: +/// +/// This implementation requires the \c byte_pool facility and malloc() to be +/// set up as the ProgressChecker allocates dynamic storage during +/// initialization to store the previous pass counts. +/// +/// It is probably not a good idea to use a single ProgressChecker for both +/// manual and timer-based checking, since there is no protection in the +/// implementation for mutiple accesses to the ProgressChecker. + +#include "progress.h" +#include "ssx_dump.h" + +// The built-in timer callback + +static void +progress_callback(void *arg) +{ + ProgressChecker *checker = (ProgressChecker *)arg; + + if (progress_checker_check(checker)){ + if (0) { + progress_checker_dump(checker); + } + SSX_PANIC(PROGRESS_CHECKER_FAILED); + } +} + + +// Bit-vector operations manage the array of bits using little-endian +// protocols + +static inline void +bit_vector_set(uint8_t *vector, size_t bit) +{ + vector[bit / 8] |= (1 << (bit % 8)); +} + + +static inline void +bit_vector_clear(uint8_t *vector, size_t bit) +{ + vector[bit / 8] &= ~(1 << (bit % 8)); +} + + +static inline int +bit_vector_is_set(uint8_t *vector, size_t bit) +{ + return ((vector[bit / 8] & (1 << (bit % 8))) != 0); +} + + +// NB: We don't have a bit-vector object with a size included. For this +// application we can only call this API if we know that there is at least 1 +// bit set in the vector. + +static size_t +bit_vector_find_first_set(uint8_t *vector) +{ + size_t byte = 0; + + while (vector[byte] == 0) { + byte++; + } + + return (8 * byte) + __builtin_ffs(vector[byte]) - 1; +} + + +/// Create a progress checker +/// +/// \param checker A pointer to an uninitialized or idle ProgressChecker +/// +/// \param name An optional character string associated with the checker +/// +/// \param pass_counts An array of pass counters - the array will be cleared +/// by this API. +/// +/// \param counters The number of counters in the array +/// +/// \param callback This function is called \e every time a check is +/// completed. +/// +/// \param arg The private argument of the callback function +/// +/// For an overview of the ProgressChecker and its APIs, see the documentation +/// for the file progress.c +/// +/// Return values other than 0 are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided +/// as the \a checker argument. +/// +/// \retval -PROGRESS_CHECKER_INVALID_ARGUMENT A null (0) pointer was provided +/// as the \a pass_counts argument, or the number of \a counters can not be +/// represented as a signed integer. +/// +/// \retval -PROGRESS_CHECKER_ALLOCATION_FAILED Memory allocation of dynamic +/// memory failed. This is treated as a fatal error here. +/// +/// This API may also return or signal other errors from its implementation +/// APIs. + +int +progress_checker_create(ProgressChecker *checker, + const char* name, + uint64_t *pass_counts, + size_t counters, + ProgressCheckerCallback callback, + void *arg) +{ + int rc, bytes; + void *memory; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF((checker == 0), + PROGRESS_CHECKER_INVALID_OBJECT); + SSX_ERROR_IF((pass_counts == 0) || + (counters != (int)counters), + PROGRESS_CHECKER_INVALID_ARGUMENT); + } + + // Install and clear the counters + + checker->pass_counts = pass_counts; + memset((void *)pass_counts, 0, counters * sizeof(uint64_t)); + checker->counters = counters; + + // Allocate and clear dynamic memory + + memory = calloc(counters, sizeof(uint64_t)); + checker->saved_counts = (uint64_t *)memory; + + bytes = (counters / 8) + (counters % 8 ? 1 : 0); + checker->bit_vector_bytes = bytes; + + memory = calloc(bytes, 1); + checker->exempt = (uint8_t *)memory; + + memory = calloc(bytes, 1); + checker->failure = (uint8_t *)memory; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF((counters != 0) && + ((checker->saved_counts == 0) || + (checker->exempt == 0) || + (checker->failure == 0)), + PROGRESS_CHECKER_ALLOCATION_FAILED); + } + + // Initialize other fields + + checker->name = name; + checker->callback = callback; + checker->arg = arg; + checker->failed = 0; + checker->checks = 0; + + // Initialize the timer structure. + + rc = ssx_timer_create(&(checker->timer), + progress_callback, + (void *)checker); + if (rc) return rc; + + return 0; +} + + +/// Exempt a pass count from progress checking +/// +/// \param checker A pointer to an initialized ProgressChecker +/// +/// \param counter The index of the counter to exempt +/// +/// For an overview of the ProgressChecker and its APIs, see the documentation +/// for the file progress.c +/// +/// Return values other than 0 are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided +/// as the \a checker argument. +/// +/// \retval -PROGRESS_CHECKER_INVALID_ARGUMENT The \a counter argument is not +/// valid. + +int +progress_checker_exempt(ProgressChecker *checker, + size_t counter) +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(checker == 0, + PROGRESS_CHECKER_INVALID_OBJECT); + SSX_ERROR_IF(counter >= checker->counters, + PROGRESS_CHECKER_INVALID_ARGUMENT); + } + + bit_vector_set(checker->exempt, counter); + + return 0; +} + + +/// Exempt all pass counts from progress checking +/// +/// \param checker A pointer to an initialized ProgressChecker +/// +/// For an overview of the ProgressChecker and its APIs, see the documentation +/// for the file progress.c +/// +/// This API is provided to support applications where pass-count-updating +/// processes are added dynamically. This API coule typically be called +/// immediately after progress_checker_create(). Them, as each process was +/// created it would call progress_checker_require() for the pass count. +/// +/// Return values other than 0 are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided +/// as the \a checker argument. +int +progress_checker_exempt_all(ProgressChecker *checker) +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(checker == 0, + PROGRESS_CHECKER_INVALID_OBJECT); + } + + memset(checker->exempt, -1, checker->bit_vector_bytes); + + return 0; +} + + +/// Require a pass count to update for progress checking +/// +/// \param checker A pointer to an initialized ProgressChecker +/// +/// \param counter The index of the counter to require +/// +/// For an overview of the ProgressChecker and its APIs, see the documentation +/// for the file progress.c +/// +/// Return values other than 0 are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided +/// as the \a checker argument. +/// +/// \retval -PROGRESS_CHECKER_INVALID_ARGUMENT The \a counter argument is not +/// valid. + +int +progress_checker_require(ProgressChecker *checker, + size_t counter) +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(checker == 0, + PROGRESS_CHECKER_INVALID_OBJECT); + SSX_ERROR_IF(counter >= checker->counters, + PROGRESS_CHECKER_INVALID_ARGUMENT); + } + + bit_vector_clear(checker->exempt, counter); + + return 0; +} + + +/// Require a pass count to update for progress checking avoiding races +/// +/// \param checker A pointer to an initialized ProgressChecker +/// +/// \param counter The index of the counter to require +/// +/// For an overview of the ProgressChecker and its APIs, see the documentation +/// for the file progress.c +/// +/// If a pass counter is marked "exempt" but then later marked "required", +/// there is a potential race between the update of the pass counter and the +/// next check, particularly when the checker is scheduled periodically. This +/// form of the progress_checker_require() marks the progress checker such +/// that \e all checks are deferred on the next call of +/// progress_checker_check() targeting the object in order to avoid the race. +/// +/// Return values other than 0 are errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided +/// as the \a checker argument. +/// +/// \retval -PROGRESS_CHECKER_INVALID_ARGUMENT The \a counter argument is not +/// valid. +int +progress_checker_require_defer(ProgressChecker *checker, + size_t counter) +{ + int rc; + SsxMachineContext ctx; + + ssx_critical_section_enter(SSX_CRITICAL, &ctx); + + rc = progress_checker_require(checker, counter); + checker->defer = 1; + + ssx_critical_section_exit(&ctx); + + return rc; +} + + +/// Check for progress in every required pass counter. +/// +/// \param checker A pointer to an initialized ProgressChecker +/// +/// For an overview of the ProgressChecker and its APIs, see the documentation +/// for the file progress.c +/// +/// Return values other than 0 are not necessarily errors; see \ref +/// ssx_errors +/// +/// \retval various Except for the error listed below, +/// progress_checker_check() returns the code returned by the callback +/// function. If no callback was provided when the checker was created, then 0 +/// is returned for success and -PROGRESS_CHECKER_FAILED is returned in the +/// event of a lack of progress. +/// +/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided +/// as the \a checker argument. + +int +progress_checker_check(ProgressChecker *checker) +{ + size_t i; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(checker == 0, + PROGRESS_CHECKER_INVALID_OBJECT); + } + + // Avoid doing this step unless necessary + + if (checker->failed != 0) { + checker->failed = 0; + memset((void *)checker->failure, 0, checker->bit_vector_bytes); + } + + // Check, unless checking has been deferred for 1 time by + // progress_checker_require_defer(). + + if (checker->defer) { + + checker->defer = 0; + + } else { + + SSX_ATOMIC(SSX_CRITICAL, checker->start_check = ssx_timebase_get()); + + for (i = 0; i < checker->counters; i++) { + + if ((checker->pass_counts[i] <= checker->saved_counts[i]) && + !(bit_vector_is_set(checker->exempt, i))) { + + checker->failed++; + bit_vector_set(checker->failure, i); + } + checker->saved_counts[i] = checker->pass_counts[i]; + } + + SSX_ATOMIC(SSX_CRITICAL, checker->end_check = ssx_timebase_get()); + } + + checker->checks++; + + if (checker->callback) { + return checker->callback(checker, checker->arg, checker->failed); + } else if (checker->failed != 0) { + return -PROGRESS_CHECKER_FAILED; + } else { + return 0; + } +} + + +/// Schedule progress checks (periodically) in the future. +/// +/// \param checker A pointer to an initialized ProgressChecker +/// +/// \param interval The relative time of the (first) check +/// +/// \param period If non-zero, checks will be made periodically with this +/// period. +/// +/// For an overview of the ProgressChecker and its APIs, see the documentation +/// for the file progress.c. See the SSX documentation for a discussion of +/// timer scheduling in SSX. +/// +/// Return values other than 0 errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided +/// as the \a checker argument. +/// +/// This API may also return or signal other errors from its implementation +/// APIs. + + +int +progress_checker_schedule(ProgressChecker *checker, + SsxInterval interval, + SsxInterval period) +{ + int rc; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(checker == 0, + PROGRESS_CHECKER_INVALID_OBJECT); + } + + rc = ssx_timer_cancel(&(checker->timer)); + if (rc != -SSX_TIMER_NOT_ACTIVE) return rc; + + rc = ssx_timer_schedule(&(checker->timer), interval, period); + if (rc) return rc; + + return 0; +} + + +/// Cancel all future (periodic) progress checks +/// +/// \param checker A pointer to an initialized ProgressChecker +/// +/// For an overview of the ProgressChecker and its APIs, see the documentation +/// for the file progress.c. See the SSX documentation for a discussion of +/// timer scheduling in SSX. +/// +/// Return values other than 0 errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided +/// as the \a checker argument. +/// +/// This API may also return or signal other errors from its implementation +/// APIs. + +int +progress_checker_cancel(ProgressChecker *checker) +{ + int rc; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(checker == 0, + PROGRESS_CHECKER_INVALID_OBJECT); + } + + rc = ssx_timer_cancel(&(checker->timer)); + if (rc) return rc; + + return 0; +} + + +/// Iterate over progress check failures +/// +/// \param checker A pointer to an initialized ProgressChecker +/// +/// \param counter Will return the index of the next failing counter, or -1 to +/// indicate no more failing counters. +/// +/// For an overview of the ProgressChecker and its APIs, see the documentation +/// for the file progress.c. +/// +/// Return values other than 0 errors; see \ref ssx_errors +/// +/// \retval 0 Successful completion +/// +/// \retval -PROGRESS_CHECKER_INVALID_OBJECT A null (0) pointer was provided +/// as the \a checker argument. + +int +progress_checker_next_failure(ProgressChecker *checker, int *counter) +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(checker == 0, + PROGRESS_CHECKER_INVALID_OBJECT); + } + + if (checker->failed == 0) { + *counter = -1; + return 0; + } + + *counter = bit_vector_find_first_set(checker->failure); + bit_vector_clear(checker->failure, *counter); + checker->failed--; + + return 0; +} + + +/// A standard way to print the results of a progress check failure, suitable +/// for use as a ProgressChecker callback. +/// +/// \param checker The checker - which may or may not have failed. If the +/// checker did fail, then a failure report is printed using printk(). +/// +/// \param arg The value to return in case of failure. In case of +/// success, 0 is returned. +/// +/// \param failed - The number of failed checks + +int +progress_checker_printk(ProgressChecker *checker, + void *arg, + size_t failed) +{ + int counter; + + if (!failed) { + return 0; + } + + printk("---------------------------------------------------------------\n"); + printk("-- Progress check failed for \"%s\" (%p).\n", + checker->name, checker); + printk("-- Check %zu over interval 0x%016llx - 0x%016llx\n", + checker->checks, checker->start_check, checker->end_check); + printk("-- %zu failed counter%s listed below\n", + failed, failed > 1 ? "s are" : " is"); + printk("---------------------------------------------------------------\n"); + + do { + progress_checker_next_failure(checker, &counter); + if (counter < 0) { + break; + } + printk("%4d. 0x%016llx\n", counter, checker->pass_counts[counter]); + } while (1); + + printk("---------------------------------------------------------------\n"); + + return (int)arg; +} + + +/// Call progress_checker_printk(), then create a kernel dump on failure +/// +/// \param checker The checker - which may or may not have failed. If the +/// checker did fail, then a failure report is printed using +/// progress_checker_printk(). +/// +/// \param arg The value to return in case of failure. In case of +/// success, 0 is returned. +/// +/// \param failed - The number of failed checks +/// +/// If progress_checker_printk() fails with a non-0 return code then this API +/// prints a full SSX kernel dump after the progress_checker_printk() report. +int +progress_checker_printk_dump(ProgressChecker *checker, + void *arg, + size_t failed) +{ + int rc; + SsxMachineContext ctx; + + rc = progress_checker_printk(checker, arg, failed); + if (rc != 0) { + ssx_critical_section_enter(SSX_CRITICAL, &ctx); + ssx_dump(ssxout, 0); + ssx_critical_section_exit(&ctx); + } + + return rc; +} + + +/// Dump a progress checker structure using printk() + +void +progress_checker_dump(ProgressChecker *checker) +{ + size_t i; + + printk("Dump of progress checker \"%s\" (%p)\n" + " Counters = %zu\n" + " Checks = %zu\n" + " Failed = %zu\n" + " Callback = %p(%p)\n", + checker->name, checker, checker->counters, checker->checks, + checker->failed, checker->callback, checker->arg); + + printk(" Pass Counts (%p) :\n", checker->pass_counts); + for (i = 0; i < checker->counters; i++) { + printk(" %9d%c 0x%016llx\n", + i, + bit_vector_is_set(checker->exempt, i) ? '*' : ' ', + checker->pass_counts[i]); + } + printk(" Saved Counts (%p) :\n", checker->saved_counts); + for (i = 0; i < checker->counters; i++) { + printk(" %9d 0x%016llx\n", + i, checker->saved_counts[i]); + } +} + + + + + + + + diff --git a/src/lib/ppc405lib/progress.h b/src/lib/ppc405lib/progress.h new file mode 100644 index 0000000..3454a0f --- /dev/null +++ b/src/lib/ppc405lib/progress.h @@ -0,0 +1,177 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/progress.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __PROGRESS_H__ +#define __PROGRESS_H__ + +// $Id$ + +/// \file progress.h +/// \brief Programmable progress (hang) checking + +#include <time.h> +#include "ssx.h" +#include "byte_pool.h" +#include "ssx_io.h" + +#ifndef __ASSEMBLER__ + +struct ProgressChecker; + +/// ProgressChecker callback type +/// +/// \param checker The checker that has just been checked +/// +/// \param arg The private argument provided when the checker was created +/// +/// \param failed The number of failed pass counts - 0 indicates no failures + +typedef int (*ProgressCheckerCallback)(struct ProgressChecker* checker, + void* arg, + size_t failed); + + +/// A simple progress (hang) checker. For API details see file progress.c + +typedef struct ProgressChecker { + + /// The application provided pass-count array + uint64_t *pass_counts; + + /// The number of pass-count counters in the array. + size_t counters; + + /// The (optional) name of the checker for reporting purposes. + const char *name; + + /// The (optional) checker callback. + ProgressCheckerCallback callback; + + /// The checker callback private argument + void *arg; + + /// The dynamically-allocated saved pass counts. + uint64_t *saved_counts; + + /// The dynamically-allocated exemption bit-vector + /// + /// \todo Get or implement a generic unlimited-precision bit vector + uint8_t *exempt; + + /// The dynamically-allocated failure bit-vector + uint8_t *failure; + + /// The number of bytes in the bit vector + size_t bit_vector_bytes; + + /// Defer all checking the next time progress_checker_check() is called. + /// + /// See progress_checker_require_defer() + int defer; + + /// The number of failures present in the *failure vector + size_t failed; + + /// A timer object to support time-based checking. + SsxTimer timer; + + /// The number of times progress_checker_check() has been called on the + /// object. + size_t checks; + + /// The time the last check started + SsxTimebase start_check; + + /// The time the last check ended + SsxTimebase end_check; + +} ProgressChecker; + + +int +progress_checker_create(ProgressChecker *checker, + const char *name, + uint64_t *pass_counts, + size_t counters, + ProgressCheckerCallback callback, + void *arg); + +int +progress_checker_exempt(ProgressChecker *checker, + size_t counter); + +int +progress_checker_exempt_all(ProgressChecker *checker); + +int +progress_checker_require(ProgressChecker *checker, + size_t counter); + +int +progress_checker_require_defer(ProgressChecker *checker, + size_t counter); + +int +progress_checker_check(ProgressChecker *checker); + +int +progress_checker_schedule(ProgressChecker *checker, + SsxInterval interval, + SsxInterval period); + +int +progress_checker_cancel(ProgressChecker *checker); + +int +progress_checker_next_failure(ProgressChecker *checker, int *counter); + +int +progress_checker_delete(ProgressChecker *checker); + +int +progress_checker_printk(ProgressChecker *checker, + void *arg, + size_t failed); + +int +progress_checker_printk_dump(ProgressChecker *checker, + void *arg, + size_t failed); + +void +progress_checker_dump(ProgressChecker *checker); + + +#endif /* __ASSEMBLER__ */ + +// Error/Panic codes + +#define PROGRESS_CHECKER_INVALID_OBJECT 0x00776001 +#define PROGRESS_CHECKER_INVALID_ARGUMENT 0x00776002 +#define PROGRESS_CHECKER_FAILED 0x00776003 +#define PROGRESS_CHECKER_CALLBACK_PANIC 0x00776004 +#define PROGRESS_CHECKER_INVARIANT 0x00776005 +#define PROGRESS_CHECKER_ALLOCATION_FAILED 0x00776006 + +#endif /* __PROGRESS_H__ */ diff --git a/src/lib/ppc405lib/puts.c b/src/lib/ppc405lib/puts.c new file mode 100644 index 0000000..446671a --- /dev/null +++ b/src/lib/ppc405lib/puts.c @@ -0,0 +1,119 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/puts.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id: puts.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/puts.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file puts.c +/// \brief Implementation of puts(), fputs(), putchar(), fputc() +/// +/// The implementations of these APIs are split out to save code space for +/// applications that do not require them. + +#include "ssx_io.h" + + +/// Put a character to a stream +/// +/// The fputc() function writes the byte specified by \a c (converted to an +/// unsigned char) to the stream pointed to by \a stream. On success, fputc() +/// returns the value written, i.e., the \c unsigned \c char form of \a c. +/// +/// The POSIX standard fputc() returns EOF on error; this version returns the +/// negative error code of the underlying I/O error, which will also be set in +/// the \a error field of the \a stream. Note that SSX may also be configured +/// to panic in the event of an I/O error. + +int +fputc(int c, FILE *stream) +{ + unsigned char uc = (unsigned char)c; + int rc = swrite(stream, (void *)(&uc), 1, 0); + if (rc < 0) { + return rc; + } else { + return uc; + } +} + + +/// Put a string to a stream +/// +/// The fputs() function writes the null-terminated string \a s to the stream +/// pointed to by \a stream. The terminating null byte is not written. On +/// success, fputs() returns 0. +/// +/// The POSIX standard fputc() returns EOF on error; this version returns the +/// negative error code of the underlying I/O error, which will also be set in +/// the \a error field of the \a stream. Note that SSX may also be configured +/// to panic in the event of an I/O error. + +int +fputs(const char *s, FILE *stream) +{ + return swrite(stream, s, strlen(s), 0); +} + +/// Put a string to \c stdout +/// +/// The puts() function writes the null-terminated string \a s followed by a +/// newline to \a stdout. The terminating null byte is not written. On +/// success, puts() returns 0. +/// +/// The POSIX standard fputc() returns EOF on error; this version returns the +/// negative error code of the underlying I/O error, which will also be set in +/// the \a error field of the \a stream. Note that SSX may also be configured +/// to panic in the event of an I/O error. + +int +puts(const char *s) +{ + int rc = fputs(s, stdout); + if (rc < 0) { + return rc; + } else { + rc = fputc('\n', stdout); + if (rc < 0) { + return rc; + } else { + return 0; + } + } +} + + +/// Put a character to \a stdout. +/// +/// \c putchar(c) is equivalent to <c> fputc(c, stdout) </c>. + +int +putchar(int c) +{ + return fputc(c, stdout); +} diff --git a/src/lib/ppc405lib/rtx_stdio.c b/src/lib/ppc405lib/rtx_stdio.c new file mode 100644 index 0000000..4bb1a18 --- /dev/null +++ b/src/lib/ppc405lib/rtx_stdio.c @@ -0,0 +1,149 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/rtx_stdio.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id$ + +/// \file rtx_stdio.c +/// \brief SSX I/O drivers for RTX stdio streams +/// +/// The RTX \a stdout and \a stderr components accept 1, 2 and 4-byte +/// transactions on a 32-bit OCI address and write the data to the RTX +/// job's \a stdout or \a stderr respectively. The \a stdin device is not yet +/// implemented. + +#include "ssx.h" +#include "rtx_stdio.h" + +RtxStdio rtx_stdin; +RtxStdio rtx_stdout; +RtxStdio rtx_stderr; + +int +rtx_stdio_sread(FILE *stream, void *buf, size_t count, size_t *read) +{ + SSX_PANIC(ENXIO); + return -ENXIO; +} + + +int +rtx_stdio_swrite(FILE *stream, const void *buf, + size_t count, size_t *written) +{ + RtxStdio *rtx = (RtxStdio *)stream; + size_t n; + + n = count; + while (n) { + if (n >= 4) { + out32(rtx->address, *((uint32_t *)buf)); + buf += 4; + n -= 4; + } else if (n >= 2) { + out16(rtx->address, *((uint16_t *)buf)); + buf += 2; + n -= 2; + } else { + out8(rtx->address, *((uint8_t *)buf)); + buf++; + n--; + } + } + + if (written != 0) { + *written = count; + } + + return 0; +} + + +ssize_t +rtx_stdio_fflush(FILE *stream) +{ + RtxStdio *rtx = (RtxStdio *)stream; + + out8(rtx->flush_address, 0); + return 0; +} + + +int +rtx_stdin_create(RtxStdio *stream) +{ + FILE *base = (FILE *)stream; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(stream == 0, EBADF); + } + + memset((void *)stream, 0, sizeof(RtxStdio)); + base->sread = rtx_stdio_sread; + + stream->address = RTX_STDIN; + + return 0; +} + + +int +rtx_stdout_create(RtxStdio *stream) +{ + FILE *base = (FILE *)stream; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(stream == 0, EBADF); + } + + memset((void *)stream, 0, sizeof(RtxStdio)); + base->swrite = rtx_stdio_swrite; + base->fflush = rtx_stdio_fflush; + + stream->address = RTX_STDOUT; + stream->flush_address = RTX_STDOUT_FLUSH; + + return 0; +} + + +int +rtx_stderr_create(RtxStdio *stream) +{ + FILE *base = (FILE *)stream; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(stream == 0, ENXIO); + } + + memset((void *)stream, 0, sizeof(RtxStdio)); + base->swrite = rtx_stdio_swrite; + base->fflush = rtx_stdio_fflush; + + stream->address = RTX_STDERR; + stream->flush_address = RTX_STDERR_FLUSH; + + return 0; +} + + diff --git a/src/lib/ppc405lib/rtx_stdio.h b/src/lib/ppc405lib/rtx_stdio.h new file mode 100644 index 0000000..cd2a439 --- /dev/null +++ b/src/lib/ppc405lib/rtx_stdio.h @@ -0,0 +1,74 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/rtx_stdio.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __RTX_STDIO_H__ +#define __RTX_STDIO_H__ + +// $Id$ + +/// \file rtx_stdio.h +/// \brief SSX I/O implementations for RTX stdio streams + +#include "ssx_io.h" +#include "rtx_stdio_addresses.h" + +/// A FILE structure for a RTX fake stdio stream + +typedef struct { + + /// The base class + FILE stream; + + /// The MMIO address of the RTX device for the stream + SsxAddress address; + + /// The MMIO address of the RTX device for flushing the stream; + SsxAddress flush_address; + +} RtxStdio; + +extern RtxStdio rtx_stdin; +extern RtxStdio rtx_stdout; +extern RtxStdio rtx_stderr; + +int +rtx_stdin_create(RtxStdio *stream); + +int +rtx_stdout_create(RtxStdio *stream); + +int +rtx_stderr_create(RtxStdio *stream); + +int +rtx_stdio_sread(FILE *stream, void *buf, size_t count, size_t *read); + +int +rtx_stdio_swrite(FILE *stream, const void *buf, + size_t count, size_t *written); + +int +rtx_stdio_sflush(FILE *stream); + +#endif /* __RTX_STDIO_H__ */ diff --git a/src/lib/ppc405lib/rtx_stdio_addresses.h b/src/lib/ppc405lib/rtx_stdio_addresses.h new file mode 100644 index 0000000..5359f7f --- /dev/null +++ b/src/lib/ppc405lib/rtx_stdio_addresses.h @@ -0,0 +1,55 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/rtx_stdio_addresses.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __RTX_STDIO_ADDRESSES_H__ +#define __RTX_STDIO_ADDRESSES_H__ + +// $Id$ + +/// \file rtx_stdio_addresses.h +/// \brief MMIO addresses and offsets of the rtx fake stdio model +/// +/// The RTX stdio module appears as OCI device #7? Reading 1 byte from the +/// stdin offset returns that byte from stdin. Writing 1, 2 or 4 bytes to the +/// stdout or stderr offsets causes output on that stream. Writing any single +/// byte to the 'flush' offsets flush the stdout or stderr streams. +/// +/// -*- This header is maintained as part of the PMX RTX model. -*- +/// -*- Do not edit in the SSX library as your edits will be lost. -*- + +#define RTX_STDIO_BASE 0x40060000 + +#define RTX_STDIN_OFFSET 0x00 +#define RTX_STDOUT_OFFSET 0x04 +#define RTX_STDOUT_FLUSH_OFFSET 0x08 +#define RTX_STDERR_OFFSET 0x0c +#define RTX_STDERR_FLUSH_OFFSET 0x10 + +#define RTX_STDIN (RTX_STDIO_BASE + RTX_STDIN_OFFSET) +#define RTX_STDOUT (RTX_STDIO_BASE + RTX_STDOUT_OFFSET) +#define RTX_STDOUT_FLUSH (RTX_STDIO_BASE + RTX_STDOUT_FLUSH_OFFSET) +#define RTX_STDERR (RTX_STDIO_BASE + RTX_STDERR_OFFSET) +#define RTX_STDERR_FLUSH (RTX_STDIO_BASE + RTX_STDERR_FLUSH_OFFSET) + +#endif /* __RTX_STDIO_ADDRESSES_H__ */ diff --git a/src/lib/ppc405lib/simics_stdio.c b/src/lib/ppc405lib/simics_stdio.c new file mode 100644 index 0000000..6ff1fa9 --- /dev/null +++ b/src/lib/ppc405lib/simics_stdio.c @@ -0,0 +1,174 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/simics_stdio.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id: simics_stdio.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/simics_stdio.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file simics_stdio.c +/// \brief SSX I/O drivers for Simics stdio streams +/// +/// The Simics 'stdio' component is a pseudo serial port for I/O to stdio, +/// stdout and stderr, as well as to other configurable streams. Each virtual +/// file potentially has a read port, write port and flush port. +/// +/// The write ports accept 1, 2 and 4-byte transactions on a 32-bit OCI +/// address and write the data to the associated stream. Writing any value to +/// the flush port flushes the output stream. The input ports are not yet +/// implemented. + +#include "ssx.h" +#include "simics_stdio.h" + + +SimicsStdio simics_stdin; +SimicsStdio simics_stdout; +SimicsStdio simics_stderr; + +int +simics_stdio_sread(FILE *stream, void *buf, size_t count, size_t *read) +{ + SSX_PANIC(ENXIO); + return -ENXIO; +} + + +int +simics_stdio_swrite(FILE *stream, const void *buf, + size_t count, size_t *written) +{ + SimicsStdio *simics = (SimicsStdio *)stream; + size_t n; + + n = count; + while (n) { + if (n >= 4) { + out32(simics->write_address, *((uint32_t *)buf)); + buf += 4; + n -= 4; + } else if (n >= 2) { + out16(simics->write_address, *((uint16_t *)buf)); + buf += 2; + n -= 2; + } else { + out8(simics->write_address, *((uint8_t *)buf)); + buf++; + n--; + } + } + + *written = count; + + return 0; +} + + +ssize_t +simics_stdio_fflush(FILE *stream) +{ + SimicsStdio *simics = (SimicsStdio *)stream; + + out8(simics->flush_address, 0); + return 0; +} + + +/// Create a SimicsStdio stream +/// +/// Any of the \a read_address, \a write_address or \a flush_address which are +/// non-0 specify that the stream supports the associated method. The Simics +/// I/O drivers do not require locking. + +int +simics_stdio_create(SimicsStdio* stream, + SsxAddress read_address, + SsxAddress write_address, + SsxAddress flush_address) +{ + FILE *base = (FILE *)stream; + int rc; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(stream == 0, EBADF); + } + + rc = FILE_create((FILE*)stream, 0); + + if (!rc) { + + stream->read_address = read_address; + if (read_address != 0) { + base->sread = simics_stdio_sread; + } + + stream->write_address = write_address; + if (write_address != 0) { + base->swrite = simics_stdio_swrite; + } + + stream->flush_address = flush_address; + if (flush_address != 0) { + base->fflush = simics_stdio_fflush; + } + } + + return rc; +} + + +int +simics_stdin_create(SimicsStdio *stream) +{ + return simics_stdio_create(stream, SIMICS_STDIN, 0, 0); +} + + +int +simics_stdout_create(SimicsStdio *stream) +{ + return simics_stdio_create(stream, 0, SIMICS_STDOUT, SIMICS_STDOUT_FLUSH); +} + +int +simics_stderr_create(SimicsStdio *stream) +{ + return simics_stdio_create(stream, 0, SIMICS_STDERR, SIMICS_STDERR_FLUSH); +} + +int +simics_stdfile_create(SimicsStdio *stream, int fn) +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF((fn < 0) > (fn >= SIMICS_STDFILE_STREAMS), EBADF); + } + + return simics_stdio_create(stream, + SIMICS_STDFILE_READ(fn), + SIMICS_STDFILE_WRITE(fn), + SIMICS_STDFILE_FLUSH(fn)); +} diff --git a/src/lib/ppc405lib/simics_stdio.h b/src/lib/ppc405lib/simics_stdio.h new file mode 100644 index 0000000..b9ebfcf --- /dev/null +++ b/src/lib/ppc405lib/simics_stdio.h @@ -0,0 +1,93 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/simics_stdio.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __SIMICS_STDIO_H__ +#define __SIMICS_STDIO_H__ + +// $Id: simics_stdio.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/simics_stdio.h,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file simics_stdio.h +/// \brief SSX I/O implementations for Simics stdio streams + +#include "ssx_io.h" +#include "simics_stdio_addresses.h" +#include "string.h" + +/// A FILE structure for a Simics fake stdio stream + +typedef struct { + + /// The base class + FILE stream; + + /// The MMIO address of the Simics read port for the stream + SsxAddress read_address; + + /// The MMIO address of the Simics write port for the stream + SsxAddress write_address; + + /// The MMIO address of the Simics device for flushing the stream; + SsxAddress flush_address; + +} SimicsStdio; + +extern SimicsStdio simics_stdin; +extern SimicsStdio simics_stdout; +extern SimicsStdio simics_stderr; + +int +simics_stdio_create(SimicsStdio* stream, + SsxAddress read_address, + SsxAddress write_address, + SsxAddress flush_address); + +int +simics_stdin_create(SimicsStdio *stream); + +int +simics_stdout_create(SimicsStdio *stream); + +int +simics_stderr_create(SimicsStdio *stream); + +int +simics_stdfile_create(SimicsStdio *stream, int fn); + +int +simics_stdio_sread(FILE *stream, void *buf, size_t count, size_t *read); + +int +simics_stdio_swrite(FILE *stream, const void *buf, + size_t count, size_t *written); + +int +simics_stdio_sflush(FILE *stream); + +#endif /* __SIMICS_STDIO_H__ */ diff --git a/src/lib/ppc405lib/simics_stdio_addresses.h b/src/lib/ppc405lib/simics_stdio_addresses.h new file mode 100644 index 0000000..4554bbf --- /dev/null +++ b/src/lib/ppc405lib/simics_stdio_addresses.h @@ -0,0 +1,91 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/simics_stdio_addresses.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __SIMICS_STDIO_ADDRESSES_H__ +#define __SIMICS_STDIO_ADDRESSES_H__ + +// $Id: simics_stdio_addresses.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/simics_stdio_addresses.h,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file simics_stdio_adresses.h +/// \brief MMIO addresses and offsets of the Simics fake stdio model +/// +/// The Simics 'stdio' component is a pseudo serial port for I/O to stdio, +/// stdout and stderr, as well as to other configurable streams. Each virtual +/// file potentially has a read port, write port and flush port. +/// +/// The write ports accept 1, 2 and 4-byte transactions on a 32-bit OCI +/// address and write the data to the associated stream. Writing any value to +/// the flush port flushes the output stream. The input ports are not yet +/// implemented. + +// -*- This header is maintained as part of the PMX Simics model. -*- +// -*- Do not edit in the SSX library as your edits will be lost. -*- + +#define SIMICS_STDIO_BASE 0x40060000 + +#define SIMICS_STDIN_OFFSET 0x00 +#define SIMICS_STDOUT_OFFSET 0x04 +#define SIMICS_STDOUT_FLUSH_OFFSET 0x08 +#define SIMICS_STDERR_OFFSET 0x0c +#define SIMICS_STDERR_FLUSH_OFFSET 0x10 + +#define SIMICS_STDFILE_0_OFFSET 0x14 +#define SIMICS_STDFILE_1_OFFSET 0x20 +#define SIMICS_STDFILE_2_OFFSET 0x2c +#define SIMICS_STDFILE_3_OFFSET 0x38 + +#define SIMICS_STDFILE_STREAMS 4 + +#define SIMICS_STDIN (SIMICS_STDIO_BASE + SIMICS_STDIN_OFFSET) +#define SIMICS_STDOUT (SIMICS_STDIO_BASE + SIMICS_STDOUT_OFFSET) +#define SIMICS_STDOUT_FLUSH (SIMICS_STDIO_BASE + SIMICS_STDOUT_FLUSH_OFFSET) +#define SIMICS_STDERR (SIMICS_STDIO_BASE + SIMICS_STDERR_OFFSET) +#define SIMICS_STDERR_FLUSH (SIMICS_STDIO_BASE + SIMICS_STDERR_FLUSH_OFFSET) + +#define SIMICS_STDFILE_READ_OFFSET(fn) \ + (SIMICS_STDFILE_0_OFFSET + (12 * fn) + 0x00) + +#define SIMICS_STDFILE_WRITE_OFFSET(fn) \ + (SIMICS_STDFILE_0_OFFSET + (12 * fn) + 0x04) + +#define SIMICS_STDFILE_FLUSH_OFFSET(fn) \ + (SIMICS_STDFILE_0_OFFSET + (12 * fn) + 0x08) + +#define SIMICS_STDFILE_READ(fn) \ + (SIMICS_STDIO_BASE + SIMICS_STDFILE_READ_OFFSET(fn)) + +#define SIMICS_STDFILE_WRITE(fn) \ + (SIMICS_STDIO_BASE + SIMICS_STDFILE_WRITE_OFFSET(fn)) + +#define SIMICS_STDFILE_FLUSH(fn) \ + (SIMICS_STDIO_BASE + SIMICS_STDFILE_FLUSH_OFFSET(fn)) + + +#endif /* __SIMICS_STDIO_ADDRESSES_H__ */ diff --git a/src/lib/ppc405lib/sprintf.c b/src/lib/ppc405lib/sprintf.c new file mode 100644 index 0000000..0a95416 --- /dev/null +++ b/src/lib/ppc405lib/sprintf.c @@ -0,0 +1,136 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/sprintf.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id: sprintf.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/sprintf.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file sprintf.c \brief Implementations of sprintf() and snprintf() using +/// StringStream. + +#include "ssx.h" +#include "ssx_io.h" +#include "string_stream.h" + + +// sprintf() is easy - just blindly copy in the data. + +static int +sprintf_swrite(FILE *stream, const void *buf, size_t size, size_t *written) +{ + StringStream *string = (StringStream *)stream; + + memcpy((void *)(&(string->data[string->next_write])), buf, size); + string->next_write += size; + *written = size; + return 0; +} + + +// snprintf() requires that bytes that won't fit in the array are simply +// discarded, but still accounted for. Note that vfprintf() is doing the +// high-level accounting. + +static int +snprintf_swrite(FILE *stream, const void *buf, size_t size, size_t *written) +{ + StringStream *string = (StringStream *)stream; + + size_t to_write = MIN(size, string->size - string->next_write - 1); + + memcpy((void *)(&(string->data[string->next_write])), buf, to_write); + string->next_write += to_write; + *written = size; + return 0; +} + + +// We use a StringStream to implement [v]sprintf() and [v]snprintf(). Once +// the formatting is finished the NULL terminator is added. + +int +vsprintf(char *str, const char *format, va_list argp) +{ + ssize_t rc; + StringStream stream; + + _string_stream_create(&stream, str, 1, 0, sprintf_swrite); + rc = vfprintf((FILE *)(&stream), format, argp); + stream.data[stream.next_write] = 0; + return rc; +} + + + +int +sprintf(char *str, const char *format, ...) +{ + va_list argp; + ssize_t rc; + + va_start(argp, format); + rc = vsprintf(str, format, argp); + va_end(argp); + + return rc; +} + + +int +vsnprintf(char *str, size_t size, const char *format, va_list argp) +{ + ssize_t rc; + StringStream stream; + + if (size == 0) { + return 0; + } + + _string_stream_create(&stream, str, size, 0, snprintf_swrite); + rc = vfprintf((FILE *)(&stream), format, argp); + stream.data[stream.next_write] = 0; + return rc; +} + + +int +snprintf(char *str, size_t size, const char *format, ...) +{ + va_list argp; + ssize_t rc; + + if (size == 0) { + return 0; + } + + va_start(argp, format); + rc = vsnprintf(str, size, format, argp); + va_end(argp); + + return rc; +} diff --git a/src/lib/ppc405lib/ssx_dump.c b/src/lib/ppc405lib/ssx_dump.c new file mode 100644 index 0000000..52334ab --- /dev/null +++ b/src/lib/ppc405lib/ssx_dump.c @@ -0,0 +1,233 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/ssx_dump.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id: ssx_dump.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/ssx_dump.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ssx_dump.c +/// \brief Routines for dumping SSX kernel data structures +/// +/// \note This is a quick hack to help solve a P8 bringup issue. This code is +/// PPC405 (OCC) specific, i.e., not "portable" SSX. Ideally this type of dump +/// would be implemented in an external debugging tool as well, however it's +/// simplest to implement here in the execution context. + +#include "ssx_dump.h" + +#define SSX_DUMP_UNIMPLEMENTED 0x00d86701 + +static const char* _threadState[] = { + 0, + "Suspended Runnable", + "Mapped", + "Suspended Blocked", + "Completed", + "Deleted", +}; + +static const char* _threadFlags[] = { + 0, + "Semaphore Pend", + "Timer Pend", + "Timer Pend | Semaphore Pend", + "Timed Out", + "Timed Out | Semaphore Pend", + "Timed Out | Timer Pend", + "Timed Out | Timer Pend | Semaphore Pend", +}; + + +static void +_dumpTimer(FILE* stream, SsxTimer* timer) +{ + fprintf(stream, + "-- Timer @ %p\n" + "-- Deque.previous = %p\n" + "-- Deque.next = %p\n" + "-- Timeout = 0x%016llx\n" + "-- Period = 0x%016llx\n" + "-- Callback = %p\n" + "-- Arg = %p\n" + "-- Options = 0x%02x\n", + timer, + timer->deque.previous, + timer->deque.next, + timer->timeout, + (unsigned long long)(timer->period), + timer->callback, + timer->arg, + timer->options); +} + + +static void +_dumpThread(FILE* stream, SsxThread* thread) +{ + SsxThreadContext* threadCtx; + SsxThreadContextFullIrq* threadCtxIrq; + uint32_t srr[4], lr, sp; + + fprintf(stream, + "-- Thread mapped at priority %d (%p)\n" + "-- Thread state = %s (%d)\n" + "-- Thread flags = %s (0x%02x)\n" + "-- Saved Stack Pointer = %p\n", + thread->priority, thread, + _threadState[thread->state], thread->state, + _threadFlags[thread->flags], thread->flags, + (void*)thread->saved_stack_pointer); + + if (thread->flags & SSX_THREAD_FLAG_SEMAPHORE_PEND) { + + fprintf(stream, + "-- Semaphore = %p\n", + (void*)thread->semaphore); + } + fprintf(stream, + "---------------------------------------------\n"); + + if (thread->flags & SSX_THREAD_FLAG_TIMER_PEND) { + + _dumpTimer(stream, &(thread->timer)); + fprintf(stream, + "---------------------------------------------\n"); + } + + if ((thread == ssx_current()) && !__ssx_kernel_context_any_interrupt()) { + + fprintf(stream, + "-- This thread is executing ssx_dump()\n"); + + } else { + + if (thread == ssx_current()) { + + // This is the interrupted thread, and only has its volatile + // context saved. The thread stack pointer is stored in a global + // kernel variable. + + if (__ssx_kernel_context_critical_interrupt()) { + + SSX_PANIC(SSX_DUMP_UNIMPLEMENTED); + srr[0] = srr[1] = srr[2] = srr[3] = lr = sp = 0; /* For GCC */ + + } else { + + threadCtxIrq = + (SsxThreadContextFullIrq*)__ssx_saved_sp_noncritical; + srr[0] = threadCtxIrq->srr0; + srr[1] = threadCtxIrq->srr1; + srr[2] = threadCtxIrq->srr2; + srr[3] = threadCtxIrq->srr3; + lr = threadCtxIrq->lr; + sp = threadCtxIrq->r1; + } + } else { + + // This is a fully swapped-out thread. The context is saved in + // at the stored stack pointer. + + threadCtx = (SsxThreadContext*)(thread->saved_stack_pointer); + srr[0] = threadCtx->srr0; + srr[1] = threadCtx->srr1; + srr[2] = threadCtx->srr2; + srr[3] = threadCtx->srr3; + lr = threadCtx->lr; + sp = ((uint32_t*)threadCtx->r1)[0]; + } + + fprintf(stream, + "-- SRR0: 0x%08x SRR1: 0x%08x " + "SRR2: 0x%08x SRR3: 0x%08x\n" + "-- LR: 0x%08x\n", + srr[0], srr[1], srr[2], srr[3], + lr); + + fprintf(stream, + "---------------------------------------------\n"); + + // Unwind the stack + + while (sp != 0) { + + fprintf(stream, + "-- SP: 0x%08x *LR*:0x%08x\n", + sp, ((uint32_t*)sp)[1]); + sp = ((uint32_t*)sp)[0]; + } + } +} + + + + +void +ssx_dump(FILE* stream, int options) +{ + int i, sep; + SsxThread* thread; + + fprintf(stream, + "------------------------------------------------------------\n"); + fprintf(stream, + "-- SSX Kernel Dump @ 0x%016llx\n" + "-- USPRG0 = 0x%08x\n" + "-- __ssx_run_queue = 0x%08x\n", + ssx_timebase_get(), + mfspr(SPRN_USPRG0), + __ssx_run_queue); + fprintf(stream, + "------------------------------------------------------------\n"); + + sep = 0; + + for (i = 0; i < SSX_THREADS; i++) { + + ssx_thread_at_priority(i, &thread); + if (thread) { + if (sep) { + fprintf(stream, + "*********************************************\n"); + } + _dumpThread(stream, thread); + sep = 1; + } + } + + fprintf(stream, + "------------------------------------------------------------\n"); +} + + + + + + + + diff --git a/src/lib/ppc405lib/ssx_dump.h b/src/lib/ppc405lib/ssx_dump.h new file mode 100644 index 0000000..a413214 --- /dev/null +++ b/src/lib/ppc405lib/ssx_dump.h @@ -0,0 +1,82 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/ssx_dump.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __SSX_DUMP_H__ +#define __SSX_DUMP_H__ + +// $Id: ssx_dump.h,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/ssx_dump.h,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ssx_dump.h +/// \brief Routines for dumping SSX kernel data structures + +#include "ssx.h" + +/// \defgroup ssx_dump_options Options for ssx_dump() +/// +/// No options are currently specified. +/// +/// @{ + +/// @} + +#ifndef __ASSEMBLER__ + +/// Dump the kernel state +/// +/// \param i_stream The stream to receive the dump. If the dump is being +/// generated prior to a kernel panic then this would typically be \a ssxout, +/// the stream used by printk. +/// +/// \param i_options AN OR-mask of option flags; See \ref ssx_dump_options +/// +/// The SSX kernel dump produces a formatted snapshot of the state of the +/// kernel and the mapped threads. This API does not manipulate the machine +/// context; If it is required to produce a precise snapshot then the caller +/// will need to make the call from a critical section. +/// +/// The following information is standard in the dump +/// +/// - The interrupt and thread state of the kernel +/// - The state of each thread +/// - A stack trace for each thread +/// +/// Options : TBD +/// +/// \bug There are likely several bugs in the current implementation due to +/// the assumption that the code is being called from a state in which the +/// kernel context is not changing. We don't have time to code and test the +/// most general implementation now. To guarantee correct operation the API +/// must currently be called from an SSX_CRITICAL critical section. +void +ssx_dump(FILE* stream, int options); + +#endif // __ASSEMBLER__ + +#endif // __SSX_DUMP_H__ diff --git a/src/lib/ppc405lib/ssx_io.c b/src/lib/ppc405lib/ssx_io.c new file mode 100644 index 0000000..b517aeb --- /dev/null +++ b/src/lib/ppc405lib/ssx_io.c @@ -0,0 +1,335 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/ssx_io.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id: ssx_io.c,v 1.2 2014/02/03 01:30:25 daviddu Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/ssx_io.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ssx_io.c +/// \brief SSX analog-replacement for C \<stdio.h\> and \<unistd.h\> functions +/// +/// The SSX library provides simple analogs for I/O functions found in the +/// typical C libraries and Posix standards. I/O is not considered part of the +/// SSX kernel per-se - I/O is implemented by the application creating I/O +/// abstractions and drivers using the simple SSX kernel services. [However, +/// the ssx_io library does provide a printk() function for use by kernel +/// drivers, so the lines are not completely clear.] +/// +/// This library provides a FILE structure or 'base class' to represent a +/// character stream. Abstractions for different devices will 'derive' a +/// subclass from this base class by including the FILE as the first member of +/// the subclass, and then likely adding more data members. The FILE +/// structure contains pointers to the routines that implement I/O operations +/// for derived classes. There is no requiremengt that a stream type implement +/// every possible stream operation. If an unsupported operation is invoked +/// on a stream then the call will simply return -ENXIO, unless otherwise +/// specified. +/// +/// The generic I/O calls use the same error-handling protocol as used by the +/// SSX kernel services. A configuration setting (\c SSX_ERROR_CHECK_API) +/// determines if error checks are made at all, and another setting (\c +/// SSX_ERROR_PANIC) determines whether the presence of a fatal error causes a +/// bad return code or an immediate panic. +/// +/// The following errors are never considered fatal errors: +/// +/// \b EOF Used to signal end-of-file +/// +/// \b EAGAIN Used to signal that an operation was made to a non-blocking +/// stream and the call would block. +/// +/// Following are the specifications and requirements for the stream methods +/// that implement a stream type. +/// +/// <b> int sread(FILE *stream, void *buf, size_t count, size_t *read) </b> +/// +/// The low-level read() function is named sread() here due to differences in +/// the prototype and semantics from the Unix counterpart. The sread() +/// functions attempts to read \a count bytes from the \a stream into \a buf, +/// sets the number of bytes read, and returns either 0 for success or a +/// negative error code. The only reason that sread() may terminate without +/// reading \a count bytes is if sread() also returns a negative return +/// code. +/// +/// The device-specific sread() function may assume that the \a stream, \a buf +/// and \a read parameters are non-NULL at entry, and the \a count is greater +/// than 0. These conditions are checked/guaranteed by the generic interface. +/// +/// <b> int swrite(FILE *stream, void *buf, size_t count, size_t *written) </b> +/// +/// The low-level write() function is named swrite() here due to differences +/// in the prototype and semantics from the Unix counterpart. The swrite() +/// function attempts to writes \a count bytes from buf to the \a stream, sets +/// the number of bytes written, and returns either 0 for success or a +/// negative error code. The only reason that swrite() may terminate without +/// writing \a count bytes is if write() also returns a negative return +/// code. +/// +/// The device-specific swrite() function may assume that the \a stream, \a +/// buf and \a written parameters are non-NULL at entry, and the \a count is +/// greater than 0. These conditions are checked/guaranteed by the generic +/// interface. +/// +/// <b> int fflush(FILE *stream) </b> +/// +/// SSX implements the fflush() call, even though the semantics are slightly +/// different from the Posix standard. The Posix fflush() returns either 0 +/// for success, or EOF to indicate an error. Here, fflush() returns negative +/// 'errno' codes directly in the event of errors. The Posix standard +/// specifies that calling fflush() with a NULL \a stream causes all open +/// files to be flushed - here it returns the -EBADF error. +/// +/// The stream-specific fflush() implementation should return one of the +/// 'errno' codes specified by the Posix standard in the event of +/// problems. + +#include "ssx.h" +#include "ssx_io.h" + +/// Initialize an SSX I/O FILE structure +/// +/// \param file A pointer to an uninitialized FILE +/// +/// \param flags Flags for the generic FILE +/// +/// This API is designed to be called from "constructors of classes derived +/// from FILE", e.g., the StringStream. The generic FILE structure is +/// cleared, and the flags are installed and other initialization based on the +/// flags is performed. The subclass constructors then install function +/// pointers for any of the FILE functions that they implement. +/// +/// \retval 0 Success +/// +/// \retval -FILE_INVALID_OBJECT The \a file pointer is null (0). +/// +/// \retval -FILE_INVALID_ARGUMENT The \a flags are not valid. + +int +FILE_create(FILE* file, int flags) +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(file == 0, FILE_INVALID_OBJECT); + SSX_ERROR_IF(flags & ~SSX_FILE_VALID_FLAGS, FILE_INVALID_ARGUMENT); + SSX_ERROR_IF(__builtin_popcount(flags & SSX_FILE_OP_LOCK_OPTIONS) > 1, + FILE_INVALID_ARGUMENT); + } + memset((void*)file, 0, sizeof(FILE)); + file->flags = flags; + ssx_semaphore_create(&(file->fop_sem), 1, 1); + return 0; +} + + +/// Assign an error code to a stream, returning a non-zero value for errors +/// considered fatal. + +int +ssx_io_error_set(FILE *stream, int code) +{ + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(stream == 0, EBADF); + } + stream->error = code; + if ((code == EOF) || + (code == EAGAIN)) { + return 0; + } else { + return -1; + } +} + + +static int +null_stream_sread(FILE *stream, void *buf, size_t count, size_t *read) +{ + *read = 0; + ssx_io_error_set(stream, EOF); + return EOF; +} + + +static int +null_stream_swrite(FILE *stream, const void *buf, size_t count, size_t *written) +{ + *written = count; + return 0; +} + + +static int +null_stream_fflush(FILE *stream) +{ + return 0; +} + + +/// A null stream; equivalent to Unix /dev/null +/// +/// This stream is statically initialized as it is required to be assigned to +/// \a ssxout at load time. The NULL stream does not require any locking. + +FILE _null_stream = { + .sread = null_stream_sread, + .swrite = null_stream_swrite, + .fflush = null_stream_fflush, + .error = 0, + .flags = 0 +}; + + +// It's up to the application to initialize the standard streams if it wishes +// to use them. Reading or writing a NULL stream will signal an error. + +FILE *stdin = 0; +FILE *stdout = 0; +FILE *stderr = 0; + +/// Stream used by printk() +/// +/// \a ssxout is the stream used by printk(). \a ssxout \e must be assigned +/// to a non-blocking stream as it may be called from interrupt handlers. \a +/// ssxout defaults to _null_stream, the equivalent of Unix' /dev/null. + +FILE *ssxout = &_null_stream; + + +/// /dev/null for SSX +FILE* ssxnull = &_null_stream; + + +/// Lock low-level file operations +/// +/// The decision on if and how low-level FILE operations needs to be locked is +/// made by the class derived from FILE, and may also require an argument +/// about its intended use. The minimum bar is always correctness. The next +/// bar is whether the application allows data from multiple writers to be +/// intermixed, or allows multiple readers to access the same input stream. +/// +/// A device like the StringStream used to implement sprintf() and snprintf() +/// does not require locking at all because it is only called from a single +/// context. String streams that implement circular buffers must be locked if +/// there is the possibility of multiple readers or writers, and the wrapping +/// buffer always needs to be locked. +/// +/// The lock implemented here is a lock on the low-level stream operations +/// sread(), swrite() and fflush(). + +#define LOCK_FILE_OPERATION(stream, operation) \ + ({ \ + int __rc; \ + SsxMachineContext __ctx = SSX_THREAD_MACHINE_CONTEXT_DEFAULT; /* GCC */ \ + if ((stream)->flags & SSX_FILE_OP_LOCK_OPTIONS) { \ + if ((stream)->flags & SSX_FILE_OP_LOCK_CRITICAL) { \ + ssx_critical_section_enter(SSX_CRITICAL, &__ctx); \ + } else if ((stream)->flags & SSX_FILE_OP_LOCK_NONCRITICAL) { \ + ssx_critical_section_enter(SSX_NONCRITICAL, &__ctx); \ + } else { \ + ssx_semaphore_pend(&((stream)->fop_sem), SSX_WAIT_FOREVER); \ + } \ + } \ + __rc = (operation); \ + if ((stream)->flags & SSX_FILE_OP_LOCK_OPTIONS) { \ + if ((stream)->flags & (SSX_FILE_OP_LOCK_CRITICAL | \ + SSX_FILE_OP_LOCK_NONCRITICAL)) { \ + ssx_critical_section_exit(&__ctx); \ + } else { \ + ssx_semaphore_post(&((stream)->fop_sem)); \ + } \ + } \ + __rc; \ + }) + + +/// Call the sread() operation of a stream + +int +sread(FILE *stream, void *buf, size_t count, size_t *read) +{ + ssize_t rc; + size_t _read; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF((stream == 0), EBADF); + SSX_IO_ERROR_IF(stream, (stream->sread == 0), ENXIO); + SSX_IO_ERROR_IF(stream, (buf == 0), EINVAL); + } + if (count == 0) { + if (read != 0) { + *read = 0; + } + return 0; + } + rc = LOCK_FILE_OPERATION(stream, stream->sread(stream, buf, count, &_read)); + if (read != 0) { + *read = _read; + } + return rc; +} + + +/// Call the swrite() operation of a stream + +ssize_t +swrite(FILE *stream, const void *buf, size_t count, size_t *written) +{ + ssize_t rc; + size_t _written; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF((stream == 0), EBADF); + SSX_IO_ERROR_IF(stream, (stream->swrite == 0), ENXIO); + SSX_IO_ERROR_IF(stream, (buf == 0), EINVAL); + } + if (count == 0) { + rc = 0; + _written = 0; + } else { + rc = LOCK_FILE_OPERATION(stream, + stream->swrite(stream, buf, count, &_written)); + } + if (written != 0) { + *written = _written; + } + return rc; +} + + +/// Call the fflush() operation of the stream + +int +fflush(FILE *stream) +{ + int rc; + + if (SSX_ERROR_CHECK_API) { + SSX_IO_ERROR_IF(stream, (stream == 0), EBADF); + SSX_IO_ERROR_IF(stream, (stream->fflush == 0), ENXIO); + } + rc = LOCK_FILE_OPERATION(stream, stream->fflush(stream)); + return rc; +} diff --git a/src/lib/ppc405lib/ssx_io.h b/src/lib/ppc405lib/ssx_io.h new file mode 100644 index 0000000..b30b32f --- /dev/null +++ b/src/lib/ppc405lib/ssx_io.h @@ -0,0 +1,228 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/ssx_io.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __SSX_IO_H__ +#define __SSX_IO_H__ + +// $Id: ssx_io.h,v 1.2 2014/02/03 01:30:25 daviddu Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/ssx_io.h,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file ssx_io.h +/// \brief SSX analog-replacement for C <stdio.h> and <unistd.h> functions + +#if !defined(_STDIO_H) && !defined(_UNISTD_H) + +#include "ssx_macros.h" +#include "ssx_api.h" + +#include "ctype.h" +#include "errno.h" +#include "string.h" + +#define EOF -1 + +#define FILE_INVALID_OBJECT 0x00345301 +#define FILE_INVALID_ARGUMENT 0x00345302 + +#ifndef __ASSEMBLER__ + +#include <stdarg.h> + +/// The SSX implementation of the FILE structure + +typedef struct FILE { + + /// The sread() method + int (*sread)(struct FILE *stream, void *buf, + size_t count, size_t *read); + + /// The swrite() method + int (*swrite)(struct FILE *stream, const void *buf, + size_t count, size_t *written); + + /// The fflush() method + int (*fflush)(struct FILE *stream); + + /// The last error code encountered + /// + /// This field is not set in the event of an error panic. + int error; + + /// SSX file/stream flags; See \ref ssx_file_flags + int flags; + + /// The semaphore used to lock low-level file operations if required. + SsxSemaphore fop_sem; + + /// The number of newline characters read by fgetc. + /// + /// This variable supports an SSX extension to <stdio.h>, the flines() + /// API. flines() returns the number of newline characters read by + /// fgetc(). This counter does not count newline characters read using + /// sread() on the stream directly. + size_t lines; + + /// The character pushed back by ungetc() + /// + /// If a character is back the flag SSX_FILE_HAS_CHARACTER will be + /// set. Note that characters pushed back with ungetc() are not returned + /// by any subsequent sread() call on the stream, so it is best not to mix + /// sread() and fgetc(). + unsigned char character; + +} FILE; + + +/// \defgroup ssx_file_flags SSX File/Stream Flags +/// @{ + +/// Low-level access is locked by an SSX_CRITICAL critical section +#define SSX_FILE_OP_LOCK_CRITICAL 0x1 + +/// Low-level access is locked by an SSX_NONCRITICAL critical section +#define SSX_FILE_OP_LOCK_NONCRITICAL 0x2 + +/// Low-level access is locked by a semaphore +#define SSX_FILE_OP_LOCK_SEMAPHORE 0x4 + +/// Mask of all locking options; +#define SSX_FILE_OP_LOCK_OPTIONS 0x7 + +/// The FILE has a character pushed back by ungetc() +#define SSX_FILE_HAS_CHARACTER 0xf + +/// All valid flags +#define SSX_FILE_VALID_FLAGS 0xf + +/// @} + + +extern FILE *stdin; +extern FILE *stdout; +extern FILE *stderr; + +/// SSX kernel output stream +/// +/// This stream implements the printk() API. It must be a non-blocking stream +/// so that it can be used in interrupt contexts. + +extern FILE *ssxout; + +extern FILE *ssxnull; + +int +FILE_create(FILE *stream, int flags); + +int +sread(FILE *stream, void *buf, size_t count, size_t *read); + +int +swrite(FILE *stream, const void *buf, size_t count, size_t *written); + +int +fflush(FILE *stream); + +int +vfprintf(FILE *stream, const char *format, va_list argp) + __attribute__ ((format (printf, 2, 0))); + +int +vprintf(const char *format, va_list argp) + __attribute__ ((format (printf, 1, 0))); + +int +fprintf(FILE *stream, const char *fmt, ...) + __attribute__ ((format (printf, 2, 3))); + +int +printf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); + +int +printk(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); + +int +vsprintf(char *str, const char *format, va_list argp) + __attribute__ ((format (printf, 2, 0))); + +int +sprintf(char *str, const char *format, ...) + __attribute__ ((format (printf, 2, 3))); + +int +vsnprintf(char *str, size_t size, const char *format, va_list argp) + __attribute__ ((format (printf, 3, 0))); + +int +snprintf(char *str, size_t size, const char *format, ...) + __attribute__ ((format (printf, 3, 4))); + +int +fputc(int c, FILE *stream); + +int +fputs(const char *s, FILE *stream); + +int +puts(const char *s); + +int +putchar(int c); + +int +fgetc(FILE* stream); + +int +ungetc(int c, FILE* stream); + +size_t +flines(FILE* stream); + +int +ssx_io_error_set(FILE *stream, int code); + +/// Handle I/O errors including panic configurations +#define SSX_IO_ERROR_IF(stream, condition, code) \ + do { \ + if (condition) { \ + if (ssx_io_error_set(stream, code) && SSX_ERROR_PANIC) { \ + SSX_PANIC(code); \ + } else { \ + return -(code); \ + } \ + } \ + } while (0) + +#define SSX_IO_ERROR(stream, code) SSX_IO_ERROR_IF((stream), 1, (code)) + + +#endif /* __ASSEMBLER__ */ + +#endif /* !defined(_STDIO_H) && !defined(_UNISTD_H) */ + +#endif /* __SSX_IO_H__ */ diff --git a/src/lib/ppc405lib/stdlib.c b/src/lib/ppc405lib/stdlib.c new file mode 100644 index 0000000..a15308d --- /dev/null +++ b/src/lib/ppc405lib/stdlib.c @@ -0,0 +1,115 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/stdlib.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id: stdlib.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/stdlib.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file stdlib.c +/// \brief Functions from <stdlib.h> +/// +/// \note The strtoX() APIs are defined in strtox.[ch] + +#include "ssx.h" +#include "ctype.h" +#include "libssx.h" +#include <stdlib.h> + + +/// Convert a string to a long integer - base 10 only +/// +/// atol(str) is defined here as strtol(str, 0, 10) in sympathy with the POSIX +/// standard. Note that by specification "atol() does not detect +/// errors", however here it will behave the same as the strtol() call just +/// mentioned. + +long +atol(const char *str) +{ + return strtol(str, 0, 10); +} + + +/// Convert a string to an integer - base 10 only +/// +/// atoi(str) is defined here as strtol(str, 0, 10) in sympathy with the POSIX +/// standard. Note that by specification "atoi() does not detect errors", +/// however in this implementation the long integer returned by the strtol() +/// call just mentioned is simply converted to an int. + +int +atoi(const char *str) +{ + return strtol(str, 0, 10); +} + + +/// 'Exit' an application +/// +/// An SSX application can not really 'exit'. By convention, exit(0) is the +/// same as ssx_halt(). Calling exit() with a non-zero code causes a kernel +/// panic - the exit code will be found in R3 on PowerPC. +/// +/// Note that to exit a thread, the thread can either return from the thread +/// entry routine or explicitly call ssx_complete(). exit() was implemented +/// to allow porting of the EEMBC benchmarks. + +void +exit(int status) +{ + if (status) { + SSX_PANIC(ERROR_EXIT); + } + ssx_halt(); +} + + +/// Compute the absolute value of the integer argument +int +abs(int i) +{ + return ((i < 0) ? -i : i); +} + + +/// Compute the absolute value of the long integer argument +long int +labs(long int i) +{ + return ((i < 0) ? -i : i); +} + + +/// Compute the absolute value of the long long integer argument +long long int +llabs(long long int i) +{ + return ((i < 0) ? -i : i); +} + + diff --git a/src/lib/ppc405lib/strcasecmp.c b/src/lib/ppc405lib/strcasecmp.c new file mode 100644 index 0000000..fc7585c --- /dev/null +++ b/src/lib/ppc405lib/strcasecmp.c @@ -0,0 +1,90 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/strcasecmp.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id: strcasecmp.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/strcasecmp.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file strcasecmp.c +/// \brief Implementation of strcasecmp() and strncasecmp() +/// +/// These routines are rarely used, hence broken out into a separate file to +/// save code space for most applications. + +#include "ssx.h" +#include "string.h" + +/// Compare two strings ignoring case +/// +/// The strcasecmp() function compares the two strings \a s1 and \a s2, +/// ignoring the case of the characters. It returns an integer less than, +/// equal to, or greater than zero if \a s1 is found, respectively, to be less +/// than, to match, or be greater than \a s2. + +int +strcasecmp(const char* s1, const char* s2) +{ + int rc; + + if (s1 == s2) { + rc = 0; + } else { + while(*s1 && (tolower(*s1) == tolower(*s2))) { + s1++; + s2++; + } + rc = *((unsigned char *)s1) - *((unsigned char *)s2); + } + return rc; +} + + +/// Compare a portion of two strings ignoring case +/// +/// The strncmp() function compares at most the first \n characters of the two +/// strings \a s1 and \a s2, ignoring the case of the characters. It returns +/// an integer less than, equal to, or greater than zero if (the prefix of) \a +/// s1 is found, respectively, to be less than, to match, or be greater than +/// (the prefix of) \a s2. + +int +strncasecmp(const char* s1, const char* s2, size_t n) +{ + int rc; + + if ((s1 == s2) || (n == 0)) { + rc = 0; + } else { + while(*s1 && (tolower(*s1) == tolower(*s2)) && n--) { + s1++; + s2++; + } + rc = *((unsigned char *)s1) - *((unsigned char *)s2); + } + return rc; +} diff --git a/src/lib/ppc405lib/strdup.c b/src/lib/ppc405lib/strdup.c new file mode 100644 index 0000000..6d205c7 --- /dev/null +++ b/src/lib/ppc405lib/strdup.c @@ -0,0 +1,63 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/strdup.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id: strdup.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/strdup.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file strdup.c +/// \brief Functions from <string.h> that require malloc() +/// +/// These APIs are split from string.c for the benefit of applications like +/// OCC FW that don't use malloc(). + +#include <stdlib.h> +#include <string.h> + +/// Duplicate a string +/// +/// \param s The string to duplicate +/// +/// The strdup() function returns a pointer to a new string which is a +/// duplicate of the input string \a s. Memory for the new string is obtained +/// with malloc(), and can be freed with free(). +/// +/// \returns The strdup() function returns a pointer to the duplicated string, +/// or NULL (0) if insufficient memory was available. + +char * +strdup(const char* s) +{ + char* dup; + + dup = (char*)malloc(strlen(s) + 1); + if (dup != 0) { + strcpy(dup, s); + } + return dup; +} diff --git a/src/lib/ppc405lib/string_stream.c b/src/lib/ppc405lib/string_stream.c new file mode 100644 index 0000000..1dfc439 --- /dev/null +++ b/src/lib/ppc405lib/string_stream.c @@ -0,0 +1,410 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/string_stream.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id: string_stream.c,v 1.2 2014/02/03 01:30:25 daviddu Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/string_stream.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file string_stream.c +/// \brief Implementations of string streams. +/// +/// \bug Need to work out overwrite/multiple-write protection. + +#include "ssx.h" +#include "string_stream.h" + + +// 'Flush' a string stream by making it empty + +static int +string_stream_fflush(FILE* stream) +{ + StringStream* string = (StringStream*) stream; + + string->next_read = 0; + string->next_write = 0; + string->flags &= ~STRING_STREAM_FULL; + return 0; +} + + +// Read the remaining tail (if any) of the circular buffer, assuming the +// buffer is not empty. + +static size_t +read_tail(StringStream* string, void* buf, size_t size) +{ + size_t read, remainder; + + read = 0; + if (string->next_read >= string->next_write) { + + remainder = string->size - string->next_read; + read = MIN(size, remainder); + memcpy(buf, (void *)(&(string->data[string->next_read])), read); + if (read != remainder) { + string->next_read += read; + } else { + string->next_read = 0; + } + } + return read; +} + + +// Read the area (if any) from the read pointer to the write pointer + +static size_t +read_head(StringStream* string, void* buf, size_t size) +{ + size_t read, remainder; + + read = 0; + if (string->next_write > string->next_read) { + + remainder = string->next_write - string->next_read; + read = MIN(size, remainder); + memcpy(buf, (void *)(&(string->data[string->next_read])), read); + string->next_read += read; + } + return read; +} + + +// Read as much data as possible from a circular buffer. Return -EAGAIN if the +// buffer would underflow. + +static int +string_stream_sread(FILE* stream, void* buf, size_t size, size_t* read) +{ + StringStream *string = (StringStream *)stream; + size_t were_read; + int rc; + + if ((string->next_read == string->next_write) && + !(string->flags & STRING_STREAM_FULL)) { + were_read = 0; + } else { + were_read = read_tail(string, buf, size); + if (were_read != size) { + were_read += read_head(string, buf + were_read, size - were_read); + } + } + if (were_read != 0) { + string->flags &= ~STRING_STREAM_FULL; + } + *read = were_read; + if (were_read < size) { + rc = -EAGAIN; + } else { + rc = 0; + } + return rc; +} + + + + +// Write the remaining tail (if any) of the circular buffer, assuming the +// buffer is not full. + +static size_t +write_tail(StringStream* string, const void* buf, size_t size) +{ + size_t written, remainder; + + written = 0; + if (string->next_write >= string->next_read) { + + remainder = string->size - string->next_write; // string->size is the size of the stream(our buffer) + written = MIN(size, remainder); + // memcpy(void *dest, const void *src, size_t n) from /lib/memcpy.c + memcpy((void *)(&(string->data[string->next_write])), buf, written); + if (written != remainder) { + string->next_write += written; + } else { + string->next_write = 0; + } + } + return written; +} + +// Write the area (if any) from the write pointer to the read pointer + +static size_t +write_head(StringStream* string, const void* buf, size_t size) +{ + size_t written, remainder; + + written = 0; + if (string->next_read > string->next_write) { + + remainder = string->next_read - string->next_write; + written = MIN(size, remainder); + memcpy((void *)(&(string->data[string->next_write])), buf, written); + string->next_write += written; + } + return written; +} + + +// Write as much data as possible to a circular buffer. Return -EAGAIN if the +// buffer would overflow. + +static int +circular_swrite(FILE* stream, const void* buf, size_t size, size_t* written) +{ + StringStream *string = (StringStream *)stream; + size_t wrote; + int rc; + + if (string->flags & STRING_STREAM_FULL) { + wrote = 0; + } else { + wrote = write_tail(string, buf, size); + if (wrote != size) { + wrote += write_head(string, buf + wrote, size - wrote); + } + } + if ((wrote != 0) && (string->next_read == string->next_write)) { + string->flags |= STRING_STREAM_FULL; + } + *written = wrote; + if (wrote < size) { + rc = -EAGAIN; + } else { + rc = 0; + } + return rc; +} + + +// Effectively write all data to a circular buffer with wrapping semantics. + +static int +wrapping_swrite(FILE* stream, const void* buf, size_t size, size_t* written) +{ + StringStream *string = (StringStream *)stream; + size_t wrote; + int rc; + + if (size >= string->size) { // If size of data >= size of buffer + + // If the amount of data will fill or overflow the entire buffer size + // then we effectively fill the buffer with the final bytes of data. + + string->next_read = 0; + string->next_write = 0; + string->flags |= STRING_STREAM_FULL; + memcpy((void *)string->data, buf + (size - string->size), size); + + } else { + + // If the string is not full, try to fill it with the + // circular_swrite(). + + if (string->flags & STRING_STREAM_FULL) { + wrote = 0; + rc = -EAGAIN; + } else { + rc = circular_swrite(stream, buf, size, &wrote); + } + if (rc) { + + // The string is full and we need to overflow. We know that size + // is less than the buffer size, and the next_read == next_write. + // Mark the stream not full so a new circular write will work, and + // at the end reset the full condition. + + string->flags &= ~STRING_STREAM_FULL; + rc = circular_swrite(stream, buf + wrote, size - wrote, &wrote); + if (rc) { + SSX_PANIC(STRING_STREAM_BUG); + } + string->next_read = string->next_write; + string->flags |= STRING_STREAM_FULL; + } + } + *written = size; + return 0; +} + + +static int +linear_swrite(FILE* stream, const void* buf, size_t size, size_t* written) +{ + // buf and size correspond to the data we are passing to our own buffer + uint32_t bit_0_mask = 0x80000000; + StringStream *string = (StringStream *)stream; + size_t wrote = 0; // right aligned + // int rc; + uint32_t num_bytes_written; + // uint32_t register_contents; + + + //if (wrote != size) { + // register_contents = in32(PMC_PORE_SCRATCH_REG1); + // register_contents = register_contents & bit_0_mask; + + //if (!register_contents) { + // Before writing to SRAM, flush everything so it will write to the top + // of the buffer each time + string_stream_fflush(stream); + + // Write printk statement to SRAM + // wrote will contain the number of bytes written + + wrote += write_tail(string, buf + wrote, size - wrote); + // if wrote != size, tell Tcl to read the whole buffer, When Tcl is done loop back to write_tail + // buf= buf+wrote + // size= size - wrote + // continue loop until size = 0 + + // Sync + eieio(); + + // Store "wrote" to register + // Set bit 0 to 1 + // out32(addr, data) + num_bytes_written = (uint32_t)wrote | bit_0_mask; + out32( PMC_PORE_SCRATCH_REG1 , num_bytes_written ); + + // Sync + eieio(); + //} + //} + + // outside of loop, set *written = wrote (which is equal to size once all data has been copied to our buffer) + *written = wrote; + return 0; + +} + + +int +_string_stream_create(StringStream* stream, + void* buf, size_t size, int flags, + int (*swrite)(FILE* stream, + const void* buf, + size_t size, + size_t* written)) +{ + FILE* file = (FILE*)stream; + int rc; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF((stream == 0) || + ((buf == 0) && (size != 0)), + STRING_STREAM_INVALID_ARGUMENT); + } + + rc = FILE_create(file, flags); + + if (!rc) { + file->swrite = swrite; + file->sread = string_stream_sread; + file->fflush = string_stream_fflush; + stream->data = buf; + stream->size = size; + stream->next_read = 0; + stream->next_write = 0; + stream->flags = 0; + } + return rc; +} + + +int +circular_stream_create(CircularStream* stream, + void* buf, size_t size, int flags) +{ + return _string_stream_create(stream, buf, size, flags, circular_swrite); +} + + +int +wrapping_stream_create(CircularStream* stream, + void* buf, size_t size, int flags) +{ + return _string_stream_create(stream, buf, size, flags, wrapping_swrite); +} + + +int +linear_stream_create(CircularStream* stream, + void* buf, size_t size, int flags) +{ + FILE* file = (FILE*)stream; + int rc; + + rc = _string_stream_create(stream, buf, size, flags, linear_swrite); + if (!rc) { + + file->sread = NULL; + // Write to register where location of buffer is + out32( PMC_PORE_SCRATCH_REG1, (uint32_t)buf); + } + return rc; +} + + +// InputStream uses string_stream_sread(), however returns EOF once all data +// has been read. + +static int +input_stream_sread(FILE* stream, void* buf, size_t size, size_t* read) +{ + int rc; + + rc = string_stream_sread(stream, buf, size, read); + if (rc == -EAGAIN) { + rc = EOF; + } + + return rc; +} + + +// For simplicity (and ease of maintainence) we create a normal string stream +// then overwrite a few key fields. + +int +input_stream_create(StringStream* stream, void* buf, size_t size, int flags) +{ + int rc; + + rc = _string_stream_create(stream, buf, size, flags, 0); + if (!rc) { + stream->stream.sread = input_stream_sread; + stream->stream.fflush = 0; + stream->flags = STRING_STREAM_FULL; + } + return rc; +} + + diff --git a/src/lib/ppc405lib/string_stream.h b/src/lib/ppc405lib/string_stream.h new file mode 100644 index 0000000..07891f5 --- /dev/null +++ b/src/lib/ppc405lib/string_stream.h @@ -0,0 +1,277 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/string_stream.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __STRING_STREAM_H__ +#define __STRING_STREAM_H__ + +// $Id: string_stream.h,v 1.2 2014/02/03 01:30:25 daviddu Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/string_stream.h,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file string_stream.h +/// \brief Implementations of string streams. + +#include "ssx_io.h" + +/// A string stream +/// +/// This structure is used for the public CircularStream and WrappingStream +/// types, as well as for the library-internal types used to implement +/// sprintf() and snprintf(). + +typedef struct { + + /// The base class + FILE stream; + + /// Data storage - provided by the creator + uint8_t *data; + + /// The size of the data storage + size_t size; + + /// The index of the next byte to write + size_t next_write; + + /// The index of the next byte to read + size_t next_read; + + /// Stream flags, see \ref string_stream_flags + int flags; + +} StringStream; + + +/// A StringStream with circular buffer semantics +/// +/// The swrite() method copies the input data to the stream buffer, which is +/// treated as a circular buffer. If the swrite() would overflow the buffer, +/// then as much data as possible is written and swrite returns -EAGAIN. In +/// all cases swrite() returns the number of bytes actually written to the +/// buffer. +/// +/// The sread() method copies data from the StringStream circular buffer to +/// the caller's buffer until either the caller's request is satisfied or all +/// of the immediately available data has been read from the buffer. If the +/// caller's request can not be immediately granted then as much data as +/// possible is copied and sread() returns -EAGAIN. In all cases sread() +/// returns the number of bytes actually read from the buffer. +/// +/// The fflush() method marks the buffer as empty, effectively losing any data +/// currently stored in the buffer. + +typedef StringStream CircularStream; + + +/// A StringStream with wrapping circular buffer semantics +/// +/// The swrite() method copies the input data to the stream buffer, which is +/// treated as a circular buffer. If the swrite() would overflow the buffer, +/// then unread data is overwritten with new data. If the size of the +/// swrite() exceeds the buffer length then the effect is simply to fill the +/// buffer with the final bytes of the caller's data. swrite() always returns +/// the number of bytes requested to be written. +/// +/// The sread() method copies data from the StringStream circular buffer to +/// the caller's buffer until either the caller's request is satisfied or all +/// of the immediately available data has been read from the buffer. If the +/// caller's request can not be immediately granted then as much data as +/// possible is copied and sread() returns -EAGAIN. In all cases sread() +/// returns the number of bytes actually read from the buffer. +/// +/// The fflush() method marks the buffer as empty, effectively losing any data +/// currently stored in the buffer. + +typedef StringStream WrappingStream; + + +/// Create a StringStream +/// +/// \param stream The StringStream to initialize +/// +/// \param buf The stream data buffer +/// +/// \param size The size of the data buffer in bytes +/// +/// \param flags Flags for FILE_create() +/// +/// \param swrite The function to be installed as the swrite() function for +/// the underlying FILE object. The sread() and fflush() functions are fixed. +/// +/// This API initializes a StringStream structure for use in I/O operations. +/// This API will typically only be used by the creation functions of specific +/// StringStream types. +/// +/// \retval 0 Success +/// +/// \retval -STRING_STREAM_INVALID_ARGUMENT Either \a stream is NULL (0) +/// or \a buf is NULL(0) and \a size is non-0. + +int +_string_stream_create(StringStream* stream, + void* buf, size_t size, int flags, + int (*swrite)(FILE* stream, + const void* buf, + size_t size, + size_t* written)); + +/// Create a CircularStream +/// +/// \param stream The CircularStream to initialize +/// +/// \param buf The stream data buffer +/// +/// \param size The size of the data buffer in bytes +/// +/// \param flags Flags for FILE_create() +/// +/// This API initializes a CircularStream structure for use in I/O +/// operations. Once created, the pointer to the CircularStream stream can be +/// cast to a FILE* and used for sread(), swrite(), fflush() and fprintf() +/// operations. +/// +/// \retval 0 Success +/// +/// \retval -STRING_STREAM_INVALID_ARGUMENT Either \a stream is NULL (0) +/// or \a buf is NULL(0) and \a size is non-0. + +int +circular_stream_create(CircularStream* stream, + void* buf, size_t size, int flags); + + +/// Create a WrappingStream +/// +/// \param stream The WrappingStream to initialize +/// +/// \param buf The stream data buffer +/// +/// \param size The size of the data buffer in bytes +/// +/// \param flags Flags for FILE_create() +/// +/// This API initializes a WrappingStream structure for use in I/O +/// operations. Once created, the pointer to the WrappingStream stream can be +/// cast to a FILE* and used for sread(), swrite(), fflush() and fprintf() +/// operations. +/// +/// \retval 0 Success +/// +/// \retval -STRING_STREAM_INVALID_ARGUMENT Either \a stream is NULL (0) +/// or \a buf is NULL(0) and \a size is non-0. + +int +wrapping_stream_create(CircularStream* stream, + void* buf, size_t size, int flags); + + +/// Create a LinearStream +/// +/// \param stream The LinearStream to initialize +/// +/// \param buf The stream data buffer +/// +/// \param size The size of the data buffer in bytes +/// +/// \param flags Flags for FILE_create() +/// +/// This API initializes a LinearStream structure for use in I/O +/// operations. Once created, the pointer to the WrappingStream stream can be +/// cast to a FILE* and used for sread(), swrite(), fflush() and fprintf() +/// operations. +/// +/// \retval 0 Success +/// +/// \retval -STRING_STREAM_INVALID_ARGUMENT Either \a stream is NULL (0) +/// or \a buf is NULL(0) and \a size is non-0. + + +int +linear_stream_create(CircularStream* stream, + void* buf, size_t size, int flags); + + +/// A StringStream used as a read-only input stream +/// +/// This is a CircualarStream created with a full buffer and without a write +/// method. It uses a special read method that returns EOF once the buffer is +/// empty rather than -EAGAIN. + +typedef StringStream InputStream; + + +/// Create an InputStream +/// +/// \param stream The InputStream to initialize +/// +/// \param buf The stream data buffer. +/// +/// \param size The size of the readable portion of the data buffer in bytes +/// +/// \param flags Flags for FILE_create() +/// +/// This API initializes an InputStream structure for use in input operations. +/// The stream data buffer contains \a size bytes of data which can be read. +/// Once created, the pointer to the InputStream stream can be cast to a FILE* +/// and used for sread(), fgetc() and ungetc() operations. Neither fflush() +/// nor swrite() are supported on this class of stream. The read operations +/// will succeed until \a size bytes have been returned from the stream, at +/// which point the stream will return EOF on any subsequent reads. +/// +/// \retval 0 Success +/// +/// \retval -STRING_STREAM_INVALID_ARGUMENT Either \a stream is NULL (0) +/// or \a buf is NULL(0) and \a size is non-0. + +int +input_stream_create(InputStream* stream, + void* buf, size_t size, int flags); + + +/// \defgroup string_stream_flags StringStream Flags +/// +/// @{ + +/// The StringStream is full +#define STRING_STREAM_FULL 0x1 + +/// @} + + +/// \defgroup string_stream_codes StringStream Error/Panic Codes +/// +/// @{ + +/// A bug was detected in a StringStream function +#define STRING_STREAM_BUG 0x00787701 +#define STRING_STREAM_INVALID_ARGUMENT 0x00787702 + +/// @} + + +#endif // __STRING_STREAM_H__ diff --git a/src/lib/ppc405lib/strtox.c b/src/lib/ppc405lib/strtox.c new file mode 100644 index 0000000..56493fa --- /dev/null +++ b/src/lib/ppc405lib/strtox.c @@ -0,0 +1,617 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/strtox.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id: strtox.c,v 1.2 2014/02/03 01:30:25 daviddu Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/strtox.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file strtox.c +/// \brief Implementation of strtol(), strtoul(), strtoll() and strtoull() +/// +/// <b> Standard String Conversion Routines </b> +/// +/// This file contains implementaions of strtol(), strtoul(), strtoll() and +/// strtoull(). These APIs are all called as +/// +/// \code +/// strtoX(const char* str, char** endptr, int base) +/// \endcode +/// +/// where X is +/// +/// - l : Convert to a long integer +/// - ul : Convert to an unsigned long integer +/// - ll : Convert to a long long integer +/// - ull : Convert to an unsigned long long integer +/// +/// \param str The string to convert +/// +/// \param endptr If non-null, will be set to a pointer to the portion of the +/// string following the convertable portion. If no conversion is performed +/// then the original \a str is returned here. +/// +/// \param base Either 0 to indicate that the base should be derived from +/// radix markers in the string, or a number in the range 2 to 36 inclusive. +/// +/// The APIs convert the initial portion of the string pointed to by \a str to +/// an integer, which is either a long integer (strtol), an unsigned long +/// (strtoul()), a long long (strtoll), or an unsigned long long +/// (strtoull). First, the APIs decompose the input string into three parts: +/// +/// - An initial, possibly empty, sequence of white-space characters (as +/// specified by isspace()) +/// +/// - A subject sequence interpreted as an integer represented in some radix +/// determined by the value of \a base +/// +/// - A final string of one or more unrecognized characters, including the +/// terminating null byte of the input string. +/// +/// The APIs then attempt to convert the subject sequence to an integer of the +/// required type and returns the result. +/// +/// If the value of \a base is 0, the expected form of the subject sequence is +/// that of a decimal constant, octal constant, or hexadecimal constant, any +/// of which may be preceded by a '+' or '-' sign. A decimal constant begins +/// with a non-zero digit, and consists of a sequence of decimal digits. An +/// octal constant consists of the prefix '0' optionally followed by a +/// sequence of the digits '0' to '7' only. A hexadecimal constant consists of +/// the prefix 0x or 0X followed by a sequence of the decimal digits and +/// letters 'a' (or 'A' ) to 'f' (or 'F' ) with values 10 to 15 respectively. +/// +/// If the value of \a base is between 2 and 36, the expected form of the +/// subject sequence is a sequence of letters and digits representing an +/// integer with the radix specified by base, optionally preceded by a '+' or +/// '-' sign. The letters from 'a' (or 'A' ) to 'z' (or 'Z' ) inclusive are +/// ascribed the values 10 to 35; only letters whose ascribed values are less +/// than that of base are permitted. If the value of base is 16, the +/// characters 0x or 0X may optionally precede the sequence of letters and +/// digits, following the sign if present. +/// +/// The subject sequence is defined as the longest initial subsequence of the +/// input string, starting with the first non-white-space character that is of +/// the expected form. The subject sequence contains no characters if the +/// input string is empty or consists entirely of white-space characters, or if +/// the first non-white-space character is other than a sign or a permissible +/// letter or digit. +/// +/// If the subject sequence has the expected form and the value of base is 0, +/// the sequence of characters starting with the first digit will be +/// interpreted as an integer constant. If the subject sequence has the +/// expected form and the value of base is between 2 and 36, it will be used +/// as the base for conversion, ascribing to each letter its value as given +/// above. If the subject sequence begins with a minus sign, the value +/// resulting from the conversion will be negated. A pointer to the final +/// string will be stored in the object pointed to by \a endptr, provided that +/// \a endptr is not a null pointer. +/// +/// If the subject sequence is empty or does not have the expected form, no +/// conversion is performed; the value of \a str is stored in the object +/// pointed to by \a endptr, provided that \a endptr is not a null pointer. +/// +/// Note that the unsigned APIs silently convert signed representations into +/// the equivalent unsigned number. +/// +/// Since 0, (L)LONG_MIN and (U)(L)LONG_MAX are returned on error and are +/// also valid returns on success, there is no way for an SSX application to +/// determine whether the conversion succeeded or failed (since SSX does not +/// support \a errno). For this reason it is recommended that SSX-only +/// applications use the underlying APIs _strtol(), _strtoul(), _strtoll() and +/// _strtoull(), or even better the extended APIs strtoi32(), strtou32(), +/// strtoi64() or strtou64() discussed further below. +/// +/// Upon successful completion, strtoX() returns the converted +/// value, if any. If no conversion could be performed or there was an error +/// in the base specification, 0 is returned. +/// +/// If the correct value is outside the range of representable values, +/// (L)LONG_MIN or (U)(L)LONG_MAX will be returned (according to the sign +/// and type of the value). +/// +/// Note: This specification is adapted from IEEE Std. 10003.1, 2003 Edition +/// +/// +/// <b> Underlying APIs </b> +/// +/// The APIs underlying the standard APIs are all called as +/// +/// \code +/// int _strtoX(const char* str, char** endptr, int radix, <type>* value) +/// \endcode +/// +/// where X is +/// +/// - l : Convert to a long integer +/// - ul : Convert to an unsigned long integer +/// - ll : Convert to a long long integer +/// - ull : Convert to an unsigned long long integer +/// +/// \param str The string to convert +/// +/// \param endptr If non-null, will be set to a pointer to the portion of the +/// string following the convertable portion. If no conversion is performed +/// then the original \a str is returned here. +/// +/// \param base Either 0 to indicate that the base should be derived from +/// radix markers in the string, or a number in the range 2 to 36 inclusive. +/// +/// \param value The converted value, returned as the return value of the +/// standard API. +/// +/// The return value of the underlying APIs is one of the following +/// +/// \retval 0 Success +/// +/// \retval -STRTOX_NO_CONVERSION_EMPTY No conversion was performed because the +/// string was effectively empty. +/// +/// \retval -STRTOX_NO_CONVERSION_PARSE No conversion was performed because the +/// string did not parse as an integer. +/// +/// \retval -STRTOX_INVALID_ARGUMENT No conversion was performed because the +/// \a base specification was not valid. +/// +/// \retval -STRTOX_UNDERFLOW_STRTOL1 Conversion resulted in underflow +/// +/// \retval -STRTOX_UNDERFLOW_STRTOL2 Conversion resulted in underflow +/// +/// \retval -STRTOX_UNDERFLOW_STRTOLL1 Conversion resulted in underflow +/// +/// \retval -STRTOX_UNDERFLOW_STRTOLL2 Conversion resulted in underflow +/// +/// \retval -STRTOX_OVERFLOW_STRTOL1 Conversion resulted in overflow +/// +/// \retval -STRTOX_OVERFLOW_STRTOL2 Conversion resulted in overflow +/// +/// \retval -STRTOX_OVERFLOW_STRTOLL1 Conversion resulted in overflow +/// +/// \retval -STRTOX_OVERFLOW_STRTOLL2 Conversion resulted in overflow +/// +/// +/// <b> Extended APIs </b> +/// +/// The extended APIs are the preferred way to do portable integer +/// conversion. These APIs are all called as +/// +/// \code +/// int strtoX(const char* str, char** endptr, int radix, <type>* value) +/// \endcode +/// +/// where X is +/// +/// - i32 : Convert to an int32_t +/// - u32 : Convert to a uint32_t +/// - i64 : Convert to an int64_t +/// - u64 : Convert to a uint64_t +/// +/// \param str The string to convert +/// +/// \param endptr If non-null, will be set to a pointer to the portion of the +/// string following the convertable portion. If no conversion is performed +/// then the original \a str is returned here. +/// +/// \param base Either 0 to indicate that the base should be derived from +/// radix markers in the string, or a number in the range 2 to 36 inclusive. +/// +/// \param value The converted value +/// +/// The return value of the underlying APIs is one of the following +/// +/// \retval 0 Success +/// +/// \retval -STRTOX_NO_CONVERSION_EMPTY No conversion was performed because the +/// string was effectively empty. +/// +/// \retval -STRTOX_NO_CONVERSION_PARSE No conversion was performed because the +/// string did not parse as an integer. +/// +/// \retval -STRTOX_INVALID_ARGUMENT No conversion was performed because the +/// \a base specification was not valid. +/// +/// \retval -STRTOX_UNDERFLOW_STRTOL1 Conversion resulted in underflow +/// +/// \retval -STRTOX_UNDERFLOW_STRTOL2 Conversion resulted in underflow +/// +/// \retval -STRTOX_UNDERFLOW_STRTOLL1 Conversion resulted in underflow +/// +/// \retval -STRTOX_UNDERFLOW_STRTOLL2 Conversion resulted in underflow +/// +/// \retval -STRTOX_OVERFLOW_STRTOL1 Conversion resulted in overflow +/// +/// \retval -STRTOX_OVERFLOW_STRTOL2 Conversion resulted in overflow +/// +/// \retval -STRTOX_OVERFLOW_STRTOLL1 Conversion resulted in overflow +/// +/// \retval -STRTOX_OVERFLOW_STRTOLL2 Conversion resulted in overflow +/// + +#include "ssx.h" +#include "ctype.h" +#include "libssx.h" +#include "strtox.h" + + +// Skip whitespace + +static const char * +skip_whitespace(const char *s) +{ + while (isspace(*s)) { + s++; + } + return s; +} + +// Pick up a +/- sign. This is a predicate returning 1 if the value is +// negated. + +static int +sign(const char** s) +{ + if (**s == '+') { + (*s)++; + return 0; + } else if (**s == '-') { + (*s)++; + return 1; + } else { + return 0; + } +} + + +// Look for a radix mark (0, 0[xX]). The string pointer is advanced if it is a +// hex mark (0[xX]), but not for a simple '0' which could be either the start +// of an octal constant or simply the number 0. The return value is either 8, +// 10 or 16. + +static int +radix_mark(const char** s) +{ + const char* p = *s; + + if (p[0] == '0') { + if ((p[1] == 'x') || (p[1] == 'X')) { + *s += 2; + return 16; + } else { + return 8; + } + } else { + return 10; + } +} + + +// Parse a character as a radix-base digit. Return the value of the digit or +// -1 if it is not a legal digit for the radix. + +static int +parse_digit(char c, int radix) +{ + if (isdigit(c)) { + if ((c - '0') < radix) { + return c - '0'; + } else { + return -1; + } + } else if (radix <= 10) { + return -1; + } else { + if (islower(c)) { + if ((c - 'a') < (radix - 10)) { + return c - 'a' + 10; + } else { + return -1; + } + } else if (isupper(c)) { + if ((c - 'A') < (radix - 10)) { + return c - 'A' + 10; + } else { + return -1; + } + } else { + return -1; + } + } +} + + +// The most basic API is strtox(), which converts a string to an unsigned long +// long. All of the base APIs are written in terms of this. This is legal due +// to the fact that conversion is defined to continue even in the event of +// overflow. This API may return the codes STRTOX_NO_CONVERSION_EMPTY, +// STRTOX_NO_CONVERSION_PARSE or STRTOX_INVALID_ARGUMENT, +// which the standard APIs always convert to a 0 +// return value. Otherwise the flags 'overflow' and 'negative' are used by +// the base APIs to determine how to handle special cases. + +static int +strtox(const char *str, char **endptr, int base, + unsigned long long* value, int* negative, int* overflow) +{ + const char* s; + unsigned long long new; + int rc, radix, digit; + + + do { + + s = str; + *value = 0; + *negative = 0; + *overflow = 0; + + // Initial error checks + + if ((base != 0) && ((base < 2) || (base > 36))) { + rc = STRTOX_INVALID_ARGUMENT; + break; + } + + // Skip whitespace + + s = skip_whitespace(s); + if (*s == '\0') { + rc = STRTOX_NO_CONVERSION_EMPTY; + break; + } + + // Process a +/- sign. Only one is allowed. + + *negative = sign(&s); + + // Look for a radix mark. Note that if base == 16 this will cause the + // skip of a leading 0 in the string not followed by [xX], but that's + // OK because it doesn't change the result of the conversion. + + if (base == 0) { + radix = radix_mark(&s); + } else { + radix = base; + if (radix == 16) { + radix_mark(&s); + } + } + + // Parse. Note that once overflow is detected we continue to parse + // (but ignore the data). + + rc = STRTOX_NO_CONVERSION_PARSE; + + while ((digit = parse_digit(*s, radix)) >= 0) { + s++; + if (!*overflow) { + rc = 0; + new = (*value * radix) + digit; + if (new < *value) { + *overflow = 1; + } else { + *value = new; + } + } + } + } while(0); + + if (endptr) { + if (rc == 0) { + *endptr = (char*)s; + } else { + *endptr = (char*)str; + } + } + + return rc; +} + + +/// See documentation for the file strtox.c +int +_strtol(const char* str, char** endptr, int base, long* value) +{ + int rc, negative, overflow; + unsigned long long value_ull; + + rc = strtox(str, endptr, base, &value_ull, &negative, &overflow); + if (rc) { + *value = 0; + } else { + if (overflow || (value_ull != (unsigned long)value_ull)) { + if (negative) { + rc = STRTOX_UNDERFLOW_STRTOL1; + *value = LONG_MIN; + } else { + rc = STRTOX_OVERFLOW_STRTOL1; + *value = LONG_MAX; + } + } else if (negative) { + if (value_ull > ((unsigned long long)LONG_MAX + 1ull)) { + rc = STRTOX_UNDERFLOW_STRTOL2; + *value = LONG_MIN; + } else { + *value = ~value_ull + 1; + } + } else if (value_ull > (unsigned long long)LONG_MAX) { + rc = STRTOX_OVERFLOW_STRTOL2; + *value = LONG_MAX; + } else { + *value = value_ull; + } + } + return rc; +} + + +/// See documentation for the file strtox.c +int +_strtoll(const char* str, char** endptr, int base, long long* value) +{ + int rc, negative, overflow; + unsigned long long value_ull; + + rc = strtox(str, endptr, base, &value_ull, &negative, &overflow); + if (rc) { + *value = 0; + } else { + if (overflow) { + if (negative) { + rc = STRTOX_UNDERFLOW_STRTOLL1; + *value = LLONG_MIN; + } else { + rc = STRTOX_OVERFLOW_STRTOLL1; + *value = LLONG_MAX; + } + } else if (negative) { + if (value_ull > ((unsigned long long)LLONG_MAX + 1ull)) { + rc = STRTOX_UNDERFLOW_STRTOLL2; + *value = LLONG_MIN; + } else { + *value = ~value_ull + 1; + } + } else if (value_ull > (unsigned long long)LLONG_MAX) { + rc = STRTOX_OVERFLOW_STRTOLL2; + *value = LLONG_MAX; + } else { + *value = value_ull; + } + } + return rc; +} + + +/// See documentation for the file strtox.c +int +_strtoul(const char* str, char** endptr, int base, unsigned long* value) +{ + int rc, negative, overflow; + unsigned long long value_ull; + + rc = strtox(str, endptr, base, &value_ull, &negative, &overflow); + if (rc) { + *value = 0; + } else { + if (overflow || (value_ull != (unsigned long)value_ull)) { + rc = STRTOX_OVERFLOW_STRTOUL; + *value = ULONG_MAX; + } else { + *value = value_ull; + if (negative) { + *value = ~*value + 1; + } + } + } + return rc; +} + +/// See documentation for the file strtox.c +int +_strtoull(const char* str, char** endptr, int base, unsigned long long* value) +{ + int rc, negative, overflow; + + rc = strtox(str, endptr, base, value, &negative, &overflow); + if (rc) { + *value = 0; + } else { + if (overflow) { + rc = STRTOX_OVERFLOW_STRTOULL; + *value = ULLONG_MAX; + } else { + if (negative) { + *value = ~*value + 1; + } + } + } + return rc; +} + + +/// See documentation for the file strtox.c +long int +strtol(const char* str, char** endptr, int base) +{ + long int value; + + _strtol(str, endptr, base, &value); + return value; +} + + +/// See documentation for the file strtox.c +long long int +strtoll(const char* str, char** endptr, int base) +{ + long long int value; + + _strtoll(str, endptr, base, &value); + return value; +} + + +/// See documentation for the file strtox.c +unsigned long int +strtoul(const char* str, char** endptr, int base) +{ + unsigned long int value; + + _strtoul(str, endptr, base, &value); + return value; +} + + +/// See documentation for the file strtox.c +unsigned long long int +strtoull(const char* str, char** endptr, int base) +{ + unsigned long long int value; + + _strtoull(str, endptr, base, &value); + return value; +} + + +#if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ <= 1)) + +/// Internal version of strtol() +/// +/// ppcnf-mcp5 (GCC 4.1) requires that the entry point __strtol_internal() be +/// present at certain optimization levels. This is equivalent to strtol() +/// except that it takes an extra argument that must be == 0. The \a group +/// parameter is supposed to control locale-specific thousands grouping. + +long int +__strtol_internal(const char* str, char** endptr, int base, int group) +{ + if (group != 0) { + SSX_PANIC(STRTOX_INVALID_ARGUMENT_STRTOL); + } + return strtol(str, endptr, base); +} + +#endif + diff --git a/src/lib/ppc405lib/strtox.h b/src/lib/ppc405lib/strtox.h new file mode 100644 index 0000000..db2cef2 --- /dev/null +++ b/src/lib/ppc405lib/strtox.h @@ -0,0 +1,151 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/strtox.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __STRTOX_H__ +#define __STRTOX_H__ + +// $Id: strtox.h,v 1.2 2014/02/03 01:30:25 daviddu Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/strtox.h,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file strtox.h +/// \brief Underlying and extended APIs that support strtoX macros +/// +/// See the Doxygen comments of the file strtox.c for descriptions of the +/// facilities provided by this header. + +#ifndef __ASSEMBLER__ + +#include <limits.h> + +// Error codes + +#define STRTOX_NO_CONVERSION_EMPTY 0x00787901 +#define STRTOX_NO_CONVERSION_PARSE 0x00787902 +#define STRTOX_INVALID_ARGUMENT 0x00787903 +#define STRTOX_INVALID_ARGUMENT_STRTOL 0x00787904 +#define STRTOX_UNDERFLOW_STRTOL1 0x00787905 +#define STRTOX_UNDERFLOW_STRTOL2 0x00787906 +#define STRTOX_UNDERFLOW_STRTOLL1 0x00787907 +#define STRTOX_UNDERFLOW_STRTOLL2 0x00787908 +#define STRTOX_OVERFLOW_STRTOL1 0x00787909 +#define STRTOX_OVERFLOW_STRTOL2 0x0078790a +#define STRTOX_OVERFLOW_STRTOLL1 0x0078790b +#define STRTOX_OVERFLOW_STRTOLL2 0x0078790c +#define STRTOX_OVERFLOW_STRTOUL 0x0078790d +#define STRTOX_OVERFLOW_STRTOULL 0x0078790e + +// Earlier GCC configurations (ppcnf-mcp5-gcc) are not configured to define +// these standard constants, which exist in the include tree under various +// switches and configuration settings (from <limits.h>). They are defined by +// default in later standard cross builds however (GCC 4.5, 4.6). However we +// always assume that (long long) is a 64-bit type. It's likely that this is +// the only place these constant will be used (as they are defined as the +// values for under/overflow of strtoX() conversions), however it may be +// necessary in the future to move these #defines somewhere else. + +#ifndef LLONG_MIN +# define LLONG_MIN (0x8000000000000000ll) +#endif + +#ifndef LLONG_MAX +# define LLONG_MAX (0x7fffffffffffffffll) +#endif + +#ifndef ULLONG_MAX +# define ULLONG_MAX (0xffffffffffffffffull) +#endif + +int +_strtol(const char* str, char** endptr, int base, long* value); + +int +_strtoul(const char* str, char** endptr, int base, unsigned long* value); + +int +_strtoll(const char* str, char** endptr, int base, long long* value); + +int +_strtoull(const char* str, char** endptr, int base, unsigned long long* value); + + +// The way the sizeof(long) is discovered by default depends on which version +// of gcc/cpp we're using as these macros are predefined by cpp. + +#if (__SIZEOF_LONG__ == 4) || (__LONG_MAX__ == 2147483647L) + +/// See documentation for the file strtox.c +static inline int +strtoi32(const char* str, char** endptr, int base, int32_t* value) +{ + long int value_l; + int rc; + + rc = _strtol(str, endptr, base, &value_l); + *value = value_l; + return rc; +} + +/// See documentation for the file strtox.c +static inline int +strtou32(const char* str, char** endptr, int base, uint32_t* value) +{ + unsigned long int value_ul; + int rc; + + rc = _strtoul(str, endptr, base, &value_ul); + *value = value_ul; + return rc; +} + +#else + +#error "No port of strtox.h yet for systems with sizeof(long) != 4" + +#endif + +// It is assumed that long long is always 64 bits; There is no standard macro +// for this size constant + +/// See documentation for the file strtox.c +static inline int +strtoi64(const char* str, char** endptr, int base, int64_t* value) +{ + return _strtoll(str, endptr, base, value); +} + +/// See documentation for the file strtox.c +static inline int +strtou64(const char* str, char** endptr, int base, uint64_t* value) +{ + return _strtoull(str, endptr, base, value); +} + +#endif // __ASSEMBLER__ + +#endif // __STRTOX_H__ diff --git a/src/lib/ppc405lib/sxlock.c b/src/lib/ppc405lib/sxlock.c new file mode 100644 index 0000000..35f9698 --- /dev/null +++ b/src/lib/ppc405lib/sxlock.c @@ -0,0 +1,494 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/sxlock.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id$ + +/// \file sxlock.c +/// \brief API for the SharedExclusiveLock +/// +/// The APIs in this file implement a shared-exclusive lock for SSX +/// applications. This type of lock is also called a readers-writer lock. The +/// lock is implemented in terms of SSX semaphores, so its use is limited to +/// threads willing to block for access to a resource. +/// +/// The SharedExclusiveLock allows multiple threads shared access to a +/// resource, while limiting exclusive access to a single thread. There are +/// several ways that this type of lock might be specified. The specification +/// implemented here is an "exclusive-biasing" lock. As long as the lock is +/// held or requested in exclusive mode, all new shared-mode requests will +/// block, and only exclusive accesses will be allowed. If multiple threads +/// are blocked exclusive the requests are honored in priority order (as +/// the underlying implementation is an SSX semaphore). Once the exclusive +/// lock is cleared, any/all threads blocked for shared access are released +/// simultaneously (using an SSX semaphore as a thread barrier). +/// +/// The lock is created (initialized) by sxlock_create(), which allows +/// specification of an initial value of the number of shared or exclusive +/// accesses outstanding. The lock/unlock APIs are as follows: +/// +/// - sxlock_lock_shared(SharedExclusiveLock* sxlock, SsxInterval timeout) +/// - sxlock_unlock_shared(SharedExclusiveLock* sxlock) +/// - sxlock_lock_exclusive(SharedExclusiveLock* sxlock, SsxInterval timeout) +/// - sxlock_unlock_exclusive(SharedExclusiveLock* sxlock) +/// +/// Threads must always issue *_lock() and *_unlock() requests in matched +/// pairs in order to avoid errors and deadlock. The *_lock() APIs allow +/// specification of a timeout, which may be SSX_WAIT_FOREVER to indicate no +/// timeout. The *_lock() APIs will return the code -SXLOCK_TIMED_OUT if the +/// timeout occurs before the thread acquires the resource. If called from an +/// interrupt context then the only legal timeout specification is +/// SSX_NO_WAIT (0). +/// +/// If a *_lock() request times out then the thread \e has \e not acquired the +/// resource and \e must \e not call *_unlock(). As with semaphores there is +/// no record that a thread holds a lock, so if a thread completes or is +/// deleted while holding a lock it is likely that the application will +/// deadlock. + +#include "ssx.h" +#include "sxlock.h" + +/// Create (initialize) a SharedExclusiveLock +/// +/// \param sxlock A pointer to the SharedExclusiveLock object to +/// initialize +/// +/// \param shared The initial numbers of shared accesses +/// +/// \param exclusive The initial numbers of exclusive accesses +/// +/// Create (initialize) a SharedExclusiveLock and optionally specify an +/// initial state. The initial number of shared or exclusive accesses can be +/// specified, however at most one of \a nshared and \a exclusive can be +/// non-0. If \a shared or \a exclusive are non-0 then eventually a +/// thread(s) will need to issue unmatched *_unlock() call(s) to allow +/// progress for other threads requiring the resource. +/// +/// \retval 0 Success +/// +/// \retval SXLOCK_INVALID_OBJECT The \a sxlock parameter is NULL (0) or +/// otherwise invalid. +/// +/// \retval SXLOCK_INVALID_ARGUMENT Both of the \a shared and \a exclusive +/// parameters are non-0. +/// +/// \retval others sxlock_create() may also return codes from +/// ssx_semaphore_create(), which would indicate a serious bug. + +int +sxlock_create(SharedExclusiveLock* sxlock, + SsxSemaphoreCount shared, + SsxSemaphoreCount exclusive) +{ + int rc; + + rc = 0; + do { + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(sxlock == 0, + SXLOCK_INVALID_OBJECT); + SSX_ERROR_IF((shared != 0) && (exclusive != 0), + SXLOCK_INVALID_ARGUMENT); + } + + rc = ssx_semaphore_create(&(sxlock->shared_sem), 0, 0); + if (rc) break; + + rc = ssx_semaphore_create(&(sxlock->exclusive_sem), 0, 0); + if (rc) break; + + sxlock->running_shared = shared; + sxlock->running_exclusive = exclusive; + + } while (0); + + return rc; +} + + +/// Lock a SharedExclusiveLock for shared access +/// +/// \param sxlock A pointer to the SharedExclusiveLock object to lock +/// +/// \param timeout The maximum amount of time to wait for access, or the +/// constant SSX_WAIT_FOREVER to wait forever. +/// +/// Acquire a SharedExclusiveLock for shared access, potentially blocking +/// forever or until a specified timeout if access is not immediately +/// granted. Access will be blocked as long as one or more threads request or +/// control exclusive acesss to the resource. Once the access is granted, the +/// thread maintains shared access to the resource until a subsequent call of +/// sxlock_unlock_shared(). +/// +/// Return values other than SSX_OK (0) are not necessarily errors; see \ref +/// ssx_errors +/// +/// The following return codes are non-error codes: +/// +/// \retval 0 Successful completion +/// +/// \retval -SXLOCK_TIMED_OUT Shared access was not acquired before the +/// timeout expired. +/// +/// The following return codes are error codes: +/// +/// \retval -SXLOCK_INVALID_OBJECT The \a sxlock parameter was NULL (0) or +/// otherwise invalid. +/// +/// \retval -SSX_ILLEGAL_CONTEXT The API was called from a critical +/// interrupt context. +/// +/// \retval -SSX_SEMAPHORE_PEND_WOULD_BLOCK The call was made from an +/// interrupt context (or before threads have been started), shared access was +/// not immediately available and a non-zero timeout was specified. +/// +/// \retval others This API may also return codes from SSX semaphore APIs, +/// which should be considered as non-recoverable errors. + + +int +sxlock_lock_shared(SharedExclusiveLock* sxlock, SsxInterval timeout) +{ + SsxMachineContext ctx; + int rc, pending_exclusive; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(sxlock == 0, SXLOCK_INVALID_OBJECT); + } + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + rc = 0; + do { + + // NB: This is the only way to correctly compute the number of threads + // pending exclusive, given that threads could be removed from the + // exclusive_sem by timeout. + + rc = ssx_semaphore_info_get(&(sxlock->exclusive_sem), + 0, &pending_exclusive); + if (rc) break; + + if ((sxlock->running_exclusive == 0) && (pending_exclusive == 0)) { + + // If no other thread has or is requesting exclusive access, the + // current thread gets immediate access. + + sxlock->running_shared++; + + } else { + + // If threads are running or pending exclusive, this thread must + // pend shared. The thread will be unblocked by an exclusive + // unlock, which is responsible for adjusting + // sxlock->running_shared in this case. + + rc = ssx_semaphore_pend(&(sxlock->shared_sem), timeout); + if (rc == -SSX_SEMAPHORE_PEND_TIMED_OUT) { + rc = -SXLOCK_TIMED_OUT; + } + } + + } while (0); + + ssx_critical_section_exit(&ctx); + + return rc; +} + + +/// Relase a SharedExclusiveLock from shared access +/// +/// \param sxlock A pointer to the SharedExclusiveLock object to unlock +/// +/// Release a SharedExclusiveLock from shared access, signalling that the +/// thread no longer requires or expects shared access to the resource. It is +/// an error for a thread to use this API if it has not previously locked +/// shared access by a call of sxlock_pend_shared() (or the thread is +/// unlocking a lock initialized in the shared-locked state). +/// +/// Return values other than SSX_OK (0) are errors +/// +/// \retval 0 Successful completion +/// +/// \retval -SSX_ILLEGAL_CONTEXT The API was called from a critical interrupt +/// context. +/// +/// \retval -SXLOCK_INVALID_OBJECT The \a sxlock parameter was NULL (0) or +/// otherwise invalid. +/// +/// \retval -SXLOCK_SHARED_UNDERFLOW There was apparently no matched call of +/// sxlock_lock_shared() prior to this call. +/// +/// \retval others This API may also return codes from SSX semaphore APIs, +/// which should be considered as non-recoverable errors. + +int +sxlock_unlock_shared(SharedExclusiveLock* sxlock) +{ + SsxMachineContext ctx; + int rc, pending_exclusive; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF_CRITICAL_INTERRUPT_CONTEXT(); + SSX_ERROR_IF(sxlock == 0, SXLOCK_INVALID_OBJECT); + SSX_ERROR_IF(sxlock->running_shared == 0, SXLOCK_SHARED_UNDERFLOW); + } + + if (SSX_ERROR_CHECK_KERNEL) { + SSX_PANIC_IF(sxlock->running_exclusive != 0, + SXLOCK_SHARED_EXCLUSIVE_INVARIANT); + } + + rc = 0; + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + do { + + // If this is the last shared thread running, and a thread wants + // exclusive access, grant it. Note that there may be shared requests + // pending on the shared_sem but we always give preference to + // exclusive requests. + + sxlock->running_shared--; + if (sxlock->running_shared == 0) { + + // Wake any single thread pending exclusive + + rc = ssx_semaphore_info_get(&(sxlock->exclusive_sem), + 0, &pending_exclusive); + if (rc) break; + + if (pending_exclusive != 0) { + + sxlock->running_exclusive = 1; + rc = ssx_semaphore_post(&(sxlock->exclusive_sem)); + if (rc) break; + } + } + + } while(0); + + ssx_critical_section_exit(&ctx); + + return rc; +} + + +/// Lock a SharedExclusiveLock for exclusive access +/// +/// \param sxlock A pointer to the SharedExclusiveLock object to lock +/// +/// \param timeout The maximum amount of time to wait for access, or the +/// constant SSX_WAIT_FOREVER to wait forever. +/// +/// Acquire a SharedExclusiveLock for exclusive access, potentially blocking +/// forever or until a specified timeout if access is not immediately +/// granted. Access will be blocked as long as one or more threads control +/// shared acesss to the resource, however once the thread requests exclusive +/// access all new shared access requests will block. Once the access is +/// granted, the thread maintains exclusive access to the resource until a +/// subsequent call of sxlock_unlock_exclusive(). +/// +/// Return values other than SSX_OK (0) are not necessarily errors; see \ref +/// ssx_errors +/// +/// The following return codes are non-error codes: +/// +/// \retval 0 Successful completion +/// +/// \retval -SXLOCK_TIMED_OUT Exclusive access was not acquired before the +/// timeout expired. +/// +/// The following return codes are error codes: +/// +/// \retval -SXLOCK_INVALID_OBJECT The \a sxlock parameter was NULL (0) or +/// otherwise invalid. +/// +/// \retval -SSX_ILLEGAL_CONTEXT The API was called from a critical +/// interrupt context. +/// +/// \retval -SSX_SEMAPHORE_PEND_WOULD_BLOCK The call was made from an +/// interrupt context (or before threads have been started), exclusive access +/// was not immediately available and a non-zero timeout was specified. +/// +/// \retval others This API may also return codes from SSX semaphore APIs, +/// which shoudl be considered as non-recoverable errors. + + +int +sxlock_lock_exclusive(SharedExclusiveLock* sxlock, SsxInterval timeout) +{ + SsxMachineContext ctx; + int rc, pending_exclusive, pending_shared; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF(sxlock == 0, SXLOCK_INVALID_OBJECT); + } + + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + rc = 0; + do { + + if ((sxlock->running_shared == 0) && + (sxlock->running_exclusive == 0)) { + + // If no other thread has acquired the lock, this thread gets + // immediate access. + + sxlock->running_exclusive = 1; + + } else { + + // Some other thread has acquired the lock. This thread must pend + // exclusive. In this case the sxlock->running_exclusive must be + // set by the *_unlock() operation that unblocks the thread. + + rc = ssx_semaphore_pend(&(sxlock->exclusive_sem), timeout); + if (rc == -SSX_SEMAPHORE_PEND_TIMED_OUT) { + + // This exclusive request timed out. Since the request may + // have blocked shared requests, then if this is the only + // exclusive request or thread we need to unblock any pending + // shared requests. + + if (sxlock->running_exclusive == 0) { + + rc = ssx_semaphore_info_get(&(sxlock->exclusive_sem), + 0, &pending_exclusive); + if (rc) break; + + if (pending_exclusive == 0) { + + rc = ssx_semaphore_info_get(&(sxlock->shared_sem), + 0, &pending_shared); + if (rc) break; + + if (pending_shared != 0) { + + sxlock->running_shared += pending_shared; + rc = ssx_semaphore_release_all(&(sxlock->shared_sem)); + if (rc) break; + } + } + } + + rc = -SXLOCK_TIMED_OUT; + } + } + + } while (0); + + ssx_critical_section_exit(&ctx); + + return rc; +} + + +/// Release a SharedExclusiveLock from exclusive access +/// +/// \param sxlock A pointer to the SharedExclusiveLock object to unlock +/// +/// Release a SharedExclusiveLock from exclusive access, signalling that the +/// thread no longer requires or expects exclusive access to the resource. It +/// is an error for a thread to use this API if it has not previously locked +/// exclusive access by a call of sxlock_lock_exclusive() (or the thread is +/// unlocking a lock initialized in the exclusive-locked state). +/// +/// Return values other than SSX_OK (0) are errors +/// +/// \retval 0 Successful completion +/// +/// \retval -SSX_ILLEGAL_CONTEXT The API was called from a critical interrupt +/// context. +/// +/// \retval -SXLOCK_INVALID_OBJECT The \a sxlock parameter was NULL (0) or +/// otherwise invalid. +/// +/// \retval -SXLOCK_EXCLUSIVE_UNDERFLOW There was apparently no matched call of +/// sxlock_lock_exclusive() prior to this call. +/// +/// \retval others This API may also return codes from SSX semaphore APIs, +/// which should be considered as non-recoverable errors. + +int +sxlock_unlock_exclusive(SharedExclusiveLock* sxlock) +{ + SsxMachineContext ctx; + int rc, pending_exclusive, pending_shared; + + if (SSX_ERROR_CHECK_API) { + SSX_ERROR_IF_CRITICAL_INTERRUPT_CONTEXT(); + SSX_ERROR_IF(sxlock == 0, SXLOCK_INVALID_OBJECT); + SSX_ERROR_IF(sxlock->running_exclusive != 1, SXLOCK_SHARED_UNDERFLOW); + } + + if (SSX_ERROR_CHECK_KERNEL) { + SSX_PANIC_IF(sxlock->running_shared != 0, + SXLOCK_SHARED_EXCLUSIVE_INVARIANT); + } + + rc = 0; + ssx_critical_section_enter(SSX_NONCRITICAL, &ctx); + + do { + + rc = ssx_semaphore_info_get(&(sxlock->exclusive_sem), + 0, &pending_exclusive); + if (rc) break; + + if (pending_exclusive != 0) { + + // If there are other threads pending exclusive, make the + // highest-priority one of them + // runnable. sxlock->running_exclusive remains equal to 1. + + rc = ssx_semaphore_post(&(sxlock->exclusive_sem)); + if (rc) break; + + } else { + + // Otherwise unblock any/all threads pending shared + + sxlock->running_exclusive = 0; + + rc = ssx_semaphore_info_get(&(sxlock->shared_sem), + 0, &pending_shared); + if (rc) break; + + if (pending_shared != 0) { + + sxlock->running_shared = pending_shared; + rc = ssx_semaphore_release_all(&(sxlock->shared_sem)); + if (rc) break; + } + } + } while (0); + + ssx_critical_section_exit(&ctx); + + return rc; +} diff --git a/src/lib/ppc405lib/sxlock.h b/src/lib/ppc405lib/sxlock.h new file mode 100644 index 0000000..e0fa196 --- /dev/null +++ b/src/lib/ppc405lib/sxlock.h @@ -0,0 +1,108 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/sxlock.h $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +#ifndef __SXLOCK_H__ +#define __SXLOCK_H__ + +// $Id$ + +/// \file sxlock.h +/// \brief The implementation of a SharedExclusiveLock +/// +/// The SharedExclusiveLock is documented in the comments for the file +/// sxlock.c + +// Error/panic codes + +#define SXLOCK_INVALID_OBJECT 0x00795501 +#define SXLOCK_INVALID_ARGUMENT 0x00795502 +#define SXLOCK_TIMED_OUT 0x00795503 +#define SXLOCK_SHARED_UNDERFLOW 0x00795504 +#define SXLOCK_EXCLUSIVE_UNDERFLOW 0x00795505 +#define SXLOCK_SHARED_EXCLUSIVE_INVARIANT 0x00795506 + +/// A shared-exclusive lock object (also called a readers-write lock) +/// +/// This facility is documented in the file sxlock.c + +typedef struct { + + /// A semaphore for threads requesting shared access + SsxSemaphore shared_sem; + + /// A semaphore for threads requesting exclusive access + SsxSemaphore exclusive_sem; + + /// The number of threads running shared + SsxSemaphoreCount running_shared; + + /// The number of threads running exclusive + SsxSemaphoreCount running_exclusive; + +} SharedExclusiveLock; + + +/// Static initialization of a shared-exclusive lock object +/// +/// \param[in] shared The number of threads running shared at static +/// initialization +/// +/// \param[in] exclusive The number of threads running exclusive at static +/// initialization. +/// +/// Note that it is an error to specify both \a shared and \a exclusive as +/// non-0. + +#define SXLOCK_INITIALIZATION(shared, exclusive) \ + { \ + SSX_SEMAPHORE_INITIALIZATION(0, 0), \ + SSX_SEMAPHORE_INITIALIZATION(0, 0), \ + (shared), (exclusive) \ + } + +/// Declare and initialize a shared-exclusive lock + +#define SXLOCK(sxlock, shared, exclusive) \ + SharedExclusiveLock sxlock = SXLOCK_INITIALIZATION(shared, exclusive) + + + +int +sxlock_create(SharedExclusiveLock* sxlock, + SsxSemaphoreCount shared, + SsxSemaphoreCount exclusive); + +int +sxlock_lock_shared(SharedExclusiveLock* sxlock, SsxInterval timeout); + +int +sxlock_unlock_shared(SharedExclusiveLock* sxlock); + +int +sxlock_lock_exclusive(SharedExclusiveLock* sxlock, SsxInterval timeout); + +int +sxlock_unlock_exclusive(SharedExclusiveLock* sxlock); + +#endif // __SXLOCK_H__ diff --git a/src/lib/ppc405lib/time.c b/src/lib/ppc405lib/time.c new file mode 100644 index 0000000..39d005c --- /dev/null +++ b/src/lib/ppc405lib/time.c @@ -0,0 +1,82 @@ +/* IBM_PROLOG_BEGIN_TAG */ +/* This is an automatically generated prolog. */ +/* */ +/* $Source: src/lib/ppc405lib/time.c $ */ +/* */ +/* OpenPOWER OnChipController Project */ +/* */ +/* Contributors Listed Below - COPYRIGHT 2015 */ +/* [+] International Business Machines Corp. */ +/* */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or */ +/* implied. See the License for the specific language governing */ +/* permissions and limitations under the License. */ +/* */ +/* IBM_PROLOG_END_TAG */ +// $Id: time.c,v 1.1.1.1 2013/12/11 20:49:20 bcbrock Exp $ +// $Source: /afs/awd/projects/eclipz/KnowledgeBase/.cvsroot/eclipz/chips/p8/working/procedures/lib/time.c,v $ +//----------------------------------------------------------------------------- +// *! (C) Copyright International Business Machines Corp. 2013 +// *! All Rights Reserved -- Property of IBM +// *! *** IBM Confidential *** +//----------------------------------------------------------------------------- + +/// \file time.c +/// \brief Implementation of functions from <time.h> +/// +/// The following standard APIs are currently supported: +/// +/// - clock_gettime() with the single clock id CLOCK_REALTIME +/// +/// Since SSX does not currently have any per-thread CPU time statistics, we +/// can not implement process-CPU-time-based APIs like the simple clock() or +/// clock_gettime() with CLOCK_REALTIME. + +#include "ssx.h" +#include <errno.h> +#include <time.h> + +/// Get time from a timer +/// +/// \param clock_id This must be the constant CLOCK_REALTIME defined in +/// <time.h>. +/// +/// \param tp A pointer to a struct timespec populated by this API. +/// +/// Although the Posix standard requires that clock_gettime() support +/// CLOCK_REALTIME, CLOCK_REALTIME measures standard Unix time (seconds since +/// the epoch) which is not available to SSX. SSX currently only supports +/// CLOCK_MONOTONIC, which is derived from the SSX timebase. +/// +/// \returns Either 0 for success, or -EINVAL in the event of an invalid +/// argument (unrecognized \a clock_id, NULL \a tp pointer). + +int +clock_gettime(clockid_t clock_id, struct timespec* tp) +{ + int rc; + SsxTimebase now; + + if ((clock_id != CLOCK_MONOTONIC) || (tp == 0)) { + rc = -EINVAL; + } else { + + now = ssx_timebase_get(); + tp->tv_sec = now / SSX_TIMEBASE_FREQUENCY_HZ; + tp->tv_nsec = + ((now % SSX_TIMEBASE_FREQUENCY_HZ) * 1000000000) / + SSX_TIMEBASE_FREQUENCY_HZ; + rc = 0; + } + + return rc; +} |