summaryrefslogtreecommitdiffstats
path: root/src/lib/ppc405lib/ssx_io.c
blob: b517aeb9b06db06d449414d9ce2915e8ca47a7da (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
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;
}
OpenPOWER on IntegriCloud