Added debugging
[people/xl0/gpxe.git] / src / core / buffer.c
1 /*
2  * Routines for filling a buffer with data received piecemeal, where
3  * the size of the data is not necessarily known in advance.
4  *
5  * Some protocols do not provide a mechanism for us to know the size
6  * of the file before we happen to receive a particular block
7  * (e.g. the final block in an MTFTP transfer).  In addition, some
8  * protocols (all the multicast protocols plus any TCP-based protocol)
9  * can, in theory, provide the data in any order.
10  *
11  * Rather than requiring each protocol to implement its own equivalent
12  * of "dd" to arrange the data into well-sized pieces before handing
13  * off to the image loader, we provide these generic buffer functions
14  * which assemble a file into a single contiguous block.  The whole
15  * block is then passed to the image loader.
16  *
17  *
18  * Note that the rather convoluted way of manipulating the buffer
19  * descriptors (using copy_{to,from}_phys rather than straightforward
20  * pointers) is needed to cope with operation as a PXE stack, when we
21  * may be running in real mode or 16-bit protected mode, and therefore
22  * cannot directly access arbitrary areas of memory.
23  *
24  */
25
26 #include "stddef.h"
27 #include "string.h"
28 #include "io.h"
29 #include "buffer.h"
30
31 /*
32  * Initialise a buffer
33  *
34  */
35 void init_buffer ( struct buffer *buffer, physaddr_t start, size_t len ) {
36         buffer->start = start;
37         buffer->end = start + len;
38         buffer->first_free = start;
39
40         if ( len ) {
41                 char tail = 1;
42                 copy_to_phys ( start, &tail, sizeof ( tail ) );
43         }
44
45         DBG ( "BUFFER [%x,%x) initialised\n", buffer->start, buffer->end );
46 }
47
48 /*
49  * Split a free block
50  *
51  */
52 static void split_free_block ( struct buffer_free_block *desc,
53                                physaddr_t block, physaddr_t split ) {
54         /* If split point is before start of block, do nothing */
55         if ( split <= block )
56                 return;
57
58         /* If split point is after end of block, do nothing */
59         if ( split >= desc->end )
60                 return;
61
62         DBG ( "BUFFER splitting [%x,%x) into [%x,%x) and [%x,%x)\n",
63               block, desc->end, block, split, split, desc->end );
64
65         /* Create descriptor for new free block */
66         copy_to_phys ( split, &desc->tail, sizeof ( desc->tail ) );
67         if ( ! desc->tail )
68                 copy_to_phys ( split, desc, sizeof ( *desc ) );
69
70         /* Update descriptor for old free block */
71         desc->tail = 0;
72         desc->next_free = split;
73         desc->end = split;
74         copy_to_phys ( block, desc, sizeof ( *desc ) );
75 }
76
77 /*
78  * Mark a free block as used
79  *
80  */
81 static inline void unfree_block ( struct buffer *buffer,
82                                   struct buffer_free_block *desc,
83                                   physaddr_t prev_block ) {
84         struct buffer_free_block prev_desc;
85         
86         /* If this is the first block, just update first_free */
87         if ( ! prev_block ) {
88                 DBG ( "BUFFER marking [%x,%x) as used\n",
89                       buffer->first_free, desc->end );
90                 buffer->first_free = desc->next_free;
91                 return;
92         }
93
94         /* Get descriptor for previous block (which cannot be a tail block) */
95         copy_from_phys ( &prev_desc, prev_block, sizeof ( prev_desc ) );
96
97         DBG ( "BUFFER marking [%x,%x) as used\n",
98               prev_desc.next_free, desc->end );
99
100         /* Modify descriptor for previous block and write it back */
101         prev_desc.next_free = desc->next_free;
102         copy_to_phys ( prev_block, &prev_desc, sizeof ( prev_desc ) );
103 }
104
105 /*
106  * Write data into a buffer
107  *
108  * It is the caller's responsibility to ensure that the boundaries
109  * between data blocks are more than sizeof(struct buffer_free_block)
110  * apart.  If this condition is not satisfied, data corruption will
111  * occur.
112  *
113  * Returns the offset to the first gap in the buffer.  (When the
114  * buffer is full, returns the offset to the byte past the end of the
115  * buffer.)
116  *
117  */
118 off_t fill_buffer ( struct buffer *buffer, void *data,
119                     off_t offset, size_t len ) {
120         struct buffer_free_block desc;
121         physaddr_t block, prev_block;
122         physaddr_t data_start, data_end;
123
124         /* Calculate start and end addresses of data */
125         data_start = buffer->start + offset;
126         data_end = data_start + len;
127         DBG ( "BUFFER [%x,%x) writing portion [%x,%x)\n",
128               buffer->start, buffer->end, data_start, data_end );
129
130         /* Iterate through the buffer's free blocks */
131         prev_block = 0;
132         block = buffer->first_free;
133         while ( block < buffer->end ) {
134                 /* Read block descriptor */
135                 desc.next_free = buffer->end;
136                 desc.end = buffer->end;
137                 copy_from_phys ( &desc.tail, block, sizeof ( desc.tail ) );
138                 if ( ! desc.tail )
139                         copy_from_phys ( &desc, block, sizeof ( desc ) );
140
141                 /* Split block at data start and end markers */
142                 split_free_block ( &desc, block, data_start );
143                 split_free_block ( &desc, block, data_end );
144
145                 /* Block is now either completely contained by or
146                  * completely outside the data area
147                  */
148                 if ( ( block >= data_start ) && ( block < data_end ) ) {
149                         /* Block is within the data area */
150                         unfree_block ( buffer, &desc, prev_block );
151                         copy_to_phys ( block, data + ( block - data_start ),
152                                        desc.end - block );
153                 } else {
154                         /* Block is outside the data area */
155                         prev_block = block;
156                 }
157
158                 /* Move to next free block */
159                 block = desc.next_free;
160         }
161
162         DBG ( "BUFFER [%x,%x) full up to %x\n",
163               buffer->start, buffer->end, buffer->first_free );
164
165         return ( buffer->first_free - buffer->start );
166 }