~pmdj/ubuntu/trusty/qemu/2.9+applesmc+fadtv3

« back to all changes in this revision

Viewing changes to roms/ipxe/src/core/iobuf.c

  • Committer: Phil Dennis-Jordan
  • Date: 2017-07-21 08:03:43 UTC
  • mfrom: (1.1.1)
  • Revision ID: phil@philjordan.eu-20170721080343-2yr2vdj7713czahv
New upstream release 2.9.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or
 
5
 * modify it under the terms of the GNU General Public License as
 
6
 * published by the Free Software Foundation; either version 2 of the
 
7
 * License, or any later version.
 
8
 *
 
9
 * This program is distributed in the hope that it will be useful, but
 
10
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
12
 * General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU General Public License
 
15
 * along with this program; if not, write to the Free Software
 
16
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 
17
 * 02110-1301, USA.
 
18
 *
 
19
 * You can also choose to distribute this program under the terms of
 
20
 * the Unmodified Binary Distribution Licence (as given in the file
 
21
 * COPYING.UBDL), provided that you have satisfied its requirements.
 
22
 */
 
23
 
 
24
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 
25
 
 
26
#include <stdint.h>
 
27
#include <strings.h>
 
28
#include <errno.h>
 
29
#include <ipxe/malloc.h>
 
30
#include <ipxe/iobuf.h>
 
31
 
 
32
/** @file
 
33
 *
 
34
 * I/O buffers
 
35
 *
 
36
 */
 
37
 
 
38
/**
 
39
 * Allocate I/O buffer with specified alignment and offset
 
40
 *
 
41
 * @v len       Required length of buffer
 
42
 * @v align     Physical alignment
 
43
 * @v offset    Offset from physical alignment
 
44
 * @ret iobuf   I/O buffer, or NULL if none available
 
45
 *
 
46
 * @c align will be rounded up to the nearest power of two.
 
47
 */
 
48
struct io_buffer * alloc_iob_raw ( size_t len, size_t align, size_t offset ) {
 
49
        struct io_buffer *iobuf;
 
50
        size_t padding;
 
51
        size_t threshold;
 
52
        unsigned int align_log2;
 
53
        void *data;
 
54
 
 
55
        /* Calculate padding required below alignment boundary to
 
56
         * ensure that a correctly aligned inline struct io_buffer
 
57
         * could fit (regardless of the requested offset).
 
58
         */
 
59
        padding = ( sizeof ( *iobuf ) + __alignof__ ( *iobuf ) - 1 );
 
60
 
 
61
        /* Round up requested alignment to at least the size of the
 
62
         * padding, to simplify subsequent calculations.
 
63
         */
 
64
        if ( align < padding )
 
65
                align = padding;
 
66
 
 
67
        /* Round up alignment to the nearest power of two, avoiding
 
68
         * a potentially undefined shift operation.
 
69
         */
 
70
        align_log2 = fls ( align - 1 );
 
71
        if ( align_log2 >= ( 8 * sizeof ( align ) ) )
 
72
                return NULL;
 
73
        align = ( 1UL << align_log2 );
 
74
 
 
75
        /* Calculate length threshold */
 
76
        assert ( align >= padding );
 
77
        threshold = ( align - padding );
 
78
 
 
79
        /* Allocate buffer plus an inline descriptor as a single unit,
 
80
         * unless doing so would push the total size over the
 
81
         * alignment boundary.
 
82
         */
 
83
        if ( len <= threshold ) {
 
84
 
 
85
                /* Round up buffer length to ensure that struct
 
86
                 * io_buffer is aligned.
 
87
                 */
 
88
                len += ( ( - len - offset ) & ( __alignof__ ( *iobuf ) - 1 ) );
 
89
 
 
90
                /* Allocate memory for buffer plus descriptor */
 
91
                data = malloc_dma_offset ( len + sizeof ( *iobuf ), align,
 
92
                                           offset );
 
93
                if ( ! data )
 
94
                        return NULL;
 
95
                iobuf = ( data + len );
 
96
 
 
97
        } else {
 
98
 
 
99
                /* Allocate memory for buffer */
 
100
                data = malloc_dma_offset ( len, align, offset );
 
101
                if ( ! data )
 
102
                        return NULL;
 
103
 
 
104
                /* Allocate memory for descriptor */
 
105
                iobuf = malloc ( sizeof ( *iobuf ) );
 
106
                if ( ! iobuf ) {
 
107
                        free_dma ( data, len );
 
108
                        return NULL;
 
109
                }
 
110
        }
 
111
 
 
112
        /* Populate descriptor */
 
113
        iobuf->head = iobuf->data = iobuf->tail = data;
 
114
        iobuf->end = ( data + len );
 
115
 
 
116
        return iobuf;
 
117
}
 
118
 
 
119
/**
 
120
 * Allocate I/O buffer
 
121
 *
 
122
 * @v len       Required length of buffer
 
123
 * @ret iobuf   I/O buffer, or NULL if none available
 
124
 *
 
125
 * The I/O buffer will be physically aligned on its own size (rounded
 
126
 * up to the nearest power of two).
 
127
 */
 
128
struct io_buffer * alloc_iob ( size_t len ) {
 
129
 
 
130
        /* Pad to minimum length */
 
131
        if ( len < IOB_ZLEN )
 
132
                len = IOB_ZLEN;
 
133
 
 
134
        /* Align buffer on its own size to avoid potential problems
 
135
         * with boundary-crossing DMA.
 
136
         */
 
137
        return alloc_iob_raw ( len, len, 0 );
 
138
}
 
139
 
 
140
/**
 
141
 * Free I/O buffer
 
142
 *
 
143
 * @v iobuf     I/O buffer
 
144
 */
 
145
void free_iob ( struct io_buffer *iobuf ) {
 
146
        size_t len;
 
147
 
 
148
        /* Allow free_iob(NULL) to be valid */
 
149
        if ( ! iobuf )
 
150
                return;
 
151
 
 
152
        /* Sanity checks */
 
153
        assert ( iobuf->head <= iobuf->data );
 
154
        assert ( iobuf->data <= iobuf->tail );
 
155
        assert ( iobuf->tail <= iobuf->end );
 
156
 
 
157
        /* Free buffer */
 
158
        len = ( iobuf->end - iobuf->head );
 
159
        if ( iobuf->end == iobuf ) {
 
160
 
 
161
                /* Descriptor is inline */
 
162
                free_dma ( iobuf->head, ( len + sizeof ( *iobuf ) ) );
 
163
 
 
164
        } else {
 
165
 
 
166
                /* Descriptor is detached */
 
167
                free_dma ( iobuf->head, len );
 
168
                free ( iobuf );
 
169
        }
 
170
}
 
171
 
 
172
/**
 
173
 * Ensure I/O buffer has sufficient headroom
 
174
 *
 
175
 * @v iobuf     I/O buffer
 
176
 * @v len       Required headroom
 
177
 *
 
178
 * This function currently only checks for the required headroom; it
 
179
 * does not reallocate the I/O buffer if required.  If we ever have a
 
180
 * code path that requires this functionality, it's a fairly trivial
 
181
 * change to make.
 
182
 */
 
183
int iob_ensure_headroom ( struct io_buffer *iobuf, size_t len ) {
 
184
 
 
185
        if ( iob_headroom ( iobuf ) >= len )
 
186
                return 0;
 
187
        return -ENOBUFS;
 
188
}
 
189
 
 
190
/**
 
191
 * Concatenate I/O buffers into a single buffer
 
192
 *
 
193
 * @v list      List of I/O buffers
 
194
 * @ret iobuf   Concatenated I/O buffer, or NULL on allocation failure
 
195
 *
 
196
 * After a successful concatenation, the list will be empty.
 
197
 */
 
198
struct io_buffer * iob_concatenate ( struct list_head *list ) {
 
199
        struct io_buffer *iobuf;
 
200
        struct io_buffer *tmp;
 
201
        struct io_buffer *concatenated;
 
202
        size_t len = 0;
 
203
 
 
204
        /* If the list contains only a single entry, avoid an
 
205
         * unnecessary additional allocation.
 
206
         */
 
207
        if ( list_is_singular ( list ) ) {
 
208
                iobuf = list_first_entry ( list, struct io_buffer, list );
 
209
                INIT_LIST_HEAD ( list );
 
210
                return iobuf;
 
211
        }
 
212
 
 
213
        /* Calculate total length */
 
214
        list_for_each_entry ( iobuf, list, list )
 
215
                len += iob_len ( iobuf );
 
216
 
 
217
        /* Allocate new I/O buffer */
 
218
        concatenated = alloc_iob_raw ( len, __alignof__ ( *iobuf ), 0 );
 
219
        if ( ! concatenated )
 
220
                return NULL;
 
221
 
 
222
        /* Move data to new I/O buffer */
 
223
        list_for_each_entry_safe ( iobuf, tmp, list, list ) {
 
224
                list_del ( &iobuf->list );
 
225
                memcpy ( iob_put ( concatenated, iob_len ( iobuf ) ),
 
226
                         iobuf->data, iob_len ( iobuf ) );
 
227
                free_iob ( iobuf );
 
228
        }
 
229
 
 
230
        return concatenated;
 
231
}
 
232
 
 
233
/**
 
234
 * Split I/O buffer
 
235
 *
 
236
 * @v iobuf             I/O buffer
 
237
 * @v len               Length to split into a new I/O buffer
 
238
 * @ret split           New I/O buffer, or NULL on allocation failure
 
239
 *
 
240
 * Split the first @c len bytes of the existing I/O buffer into a
 
241
 * separate I/O buffer.  The resulting buffers are likely to have no
 
242
 * headroom or tailroom.
 
243
 *
 
244
 * If this call fails, then the original buffer will be unmodified.
 
245
 */
 
246
struct io_buffer * iob_split ( struct io_buffer *iobuf, size_t len ) {
 
247
        struct io_buffer *split;
 
248
 
 
249
        /* Sanity checks */
 
250
        assert ( len <= iob_len ( iobuf ) );
 
251
 
 
252
        /* Allocate new I/O buffer */
 
253
        split = alloc_iob ( len );
 
254
        if ( ! split )
 
255
                return NULL;
 
256
 
 
257
        /* Copy in data */
 
258
        memcpy ( iob_put ( split, len ), iobuf->data, len );
 
259
        iob_pull ( iobuf, len );
 
260
        return split;
 
261
}