5eef50866f0ed73bc511268b108234e24a1ed4ea
[people/indolent/gpxe.git/.git] / src / arch / i386 / core / umalloc.c
1 /*
2  * Copyright (C) 2007 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., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 /**
20  * @file
21  *
22  * External memory allocation
23  *
24  */
25
26 #include <gpxe/uaccess.h>
27 #include <gpxe/hidemem.h>
28 #include <gpxe/umalloc.h>
29
30 /** Alignment of external allocated memory */
31 #define EM_ALIGN ( 4 * 1024 )
32
33 /** Equivalent of NOWHERE for user pointers */
34 #define UNOWHERE ( ~UNULL )
35
36 /** Start of Etherboot text, as defined by the linker */
37 extern char _text[];
38
39 /** Top of allocatable memory */
40 #define TOP ( virt_to_user ( _text ) )
41
42 /** An external memory block */
43 struct external_memory {
44         /** Size of this memory block (excluding this header) */
45         size_t size;
46         /** Block is currently in use */
47         int used;
48 };
49
50 /** Current lowest allocated block
51  *
52  * A value of UNULL indicates that no blocks are currently allocated.
53  */
54 userptr_t bottom = UNULL;
55
56 /**
57  * Collect free blocks
58  *
59  */
60 static void ecollect_free ( void ) {
61         struct external_memory extmem;
62
63         /* Walk the free list and collect empty blocks */
64         while ( bottom != TOP ) {
65                 copy_from_user ( &extmem, bottom, -sizeof ( extmem ),
66                                  sizeof ( extmem ) );
67                 if ( extmem.used )
68                         break;
69                 DBG ( "EXTMEM freeing [%lx,%lx)\n", user_to_phys ( bottom, 0 ),
70                       user_to_phys ( bottom, extmem.size ) );
71                 bottom = userptr_add ( bottom,
72                                        ( extmem.size + sizeof ( extmem ) ) );
73         }
74 }
75
76 /**
77  * Reallocate external memory
78  *
79  * @v old_ptr           Memory previously allocated by umalloc(), or UNULL
80  * @v new_size          Requested size
81  * @ret new_ptr         Allocated memory, or UNULL
82  *
83  * Calling realloc() with a new size of zero is a valid way to free a
84  * memory block.
85  */
86 userptr_t urealloc ( userptr_t ptr, size_t new_size ) {
87         struct external_memory extmem;
88         userptr_t new = ptr;
89         size_t align;
90
91         /* Initialise external memory allocator if necessary */
92         if ( ! bottom  )
93                 bottom = TOP;
94
95         /* Get block properties into extmem */
96         if ( ptr && ( ptr != UNOWHERE ) ) {
97                 /* Determine old size */
98                 copy_from_user ( &extmem, ptr, -sizeof ( extmem ),
99                                  sizeof ( extmem ) );
100         } else {
101                 /* Create a zero-length block */
102                 ptr = bottom = userptr_add ( bottom, -sizeof ( extmem ) );
103                 DBG ( "EXTMEM allocating [%lx,%lx)\n",
104                       user_to_phys ( ptr, 0 ), user_to_phys ( ptr, 0 ) );
105                 extmem.size = 0;
106         }
107         extmem.used = ( new_size > 0 );
108
109         /* Expand/shrink block if possible */
110         if ( ptr == bottom ) {
111                 /* Update block */
112                 new = userptr_add ( ptr, - ( new_size - extmem.size ) );
113                 align = ( user_to_phys ( new, 0 ) & ( EM_ALIGN - 1 ) );
114                 new_size += align;
115                 new = userptr_add ( new, -align );
116                 DBG ( "EXTMEM expanding [%lx,%lx) to [%lx,%lx)\n",
117                       user_to_phys ( ptr, 0 ),
118                       user_to_phys ( ptr, extmem.size ),
119                       user_to_phys ( new, 0 ),
120                       user_to_phys ( new, new_size ));
121                 memmove_user ( new, 0, ptr, 0, ( ( extmem.size < new_size ) ?
122                                                  extmem.size : new_size ) );
123                 extmem.size = new_size;
124                 bottom = new;
125         } else {
126                 /* Cannot expand; can only pretend to shrink */
127                 if ( new_size > extmem.size ) {
128                         /* Refuse to expand */
129                         DBG ( "EXTMEM cannot expand [%lx,%lx)\n",
130                               user_to_phys ( ptr, 0 ),
131                               user_to_phys ( ptr, extmem.size ) );
132                         return UNULL;
133                 }
134         }
135
136         /* Write back block properties */
137         copy_to_user ( new, -sizeof ( extmem ), &extmem,
138                        sizeof ( extmem ) );
139
140         /* Collect any free blocks and update hidden memory region */
141         ecollect_free();
142         hide_region ( EXTMEM, user_to_phys ( bottom, -sizeof ( extmem ) ),
143                       user_to_phys ( TOP, 0 ) );
144
145         return ( new_size ? new : UNOWHERE );
146 }
147
148 /**
149  * Allocate external memory
150  *
151  * @v size              Requested size
152  * @ret ptr             Memory, or UNULL
153  *
154  * Memory is guaranteed to be aligned to a page boundary.
155  */
156 userptr_t umalloc ( size_t size ) {
157         return urealloc ( UNULL, size );
158 }
159
160 /**
161  * Free external memory
162  *
163  * @v ptr               Memory allocated by umalloc(), or UNULL
164  *
165  * If @c ptr is UNULL, no action is taken.
166  */
167 void ufree ( userptr_t ptr ) {
168         urealloc ( ptr, 0 );
169 }