Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / arch / i386 / interface / pcbios / memtop_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., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  */
19
20 FILE_LICENCE ( GPL2_OR_LATER );
21
22 /**
23  * @file
24  *
25  * External memory allocation
26  *
27  */
28
29 #include <limits.h>
30 #include <errno.h>
31 #include <ipxe/uaccess.h>
32 #include <ipxe/hidemem.h>
33 #include <ipxe/io.h>
34 #include <ipxe/memblock.h>
35 #include <ipxe/umalloc.h>
36
37 /** Alignment of external allocated memory */
38 #define EM_ALIGN ( 4 * 1024 )
39
40 /** Equivalent of NOWHERE for user pointers */
41 #define UNOWHERE ( ~UNULL )
42
43 /** An external memory block */
44 struct external_memory {
45         /** Size of this memory block (excluding this header) */
46         size_t size;
47         /** Block is currently in use */
48         int used;
49 };
50
51 /** Top of heap */
52 static userptr_t top = UNULL;
53
54 /** Bottom of heap (current lowest allocated block) */
55 static userptr_t bottom = UNULL;
56
57 /** Remaining space on heap */
58 static size_t heap_size;
59
60 /**
61  * Initialise external heap
62  *
63  */
64 static void init_eheap ( void ) {
65         userptr_t base;
66
67         heap_size = largest_memblock ( &base );
68         bottom = top = userptr_add ( base, heap_size );
69         DBG ( "External heap grows downwards from %lx (size %zx)\n",
70               user_to_phys ( top, 0 ), heap_size );
71 }
72
73 /**
74  * Collect free blocks
75  *
76  */
77 static void ecollect_free ( void ) {
78         struct external_memory extmem;
79         size_t len;
80
81         /* Walk the free list and collect empty blocks */
82         while ( bottom != top ) {
83                 copy_from_user ( &extmem, bottom, -sizeof ( extmem ),
84                                  sizeof ( extmem ) );
85                 if ( extmem.used )
86                         break;
87                 DBG ( "EXTMEM freeing [%lx,%lx)\n", user_to_phys ( bottom, 0 ),
88                       user_to_phys ( bottom, extmem.size ) );
89                 len = ( extmem.size + sizeof ( extmem ) );
90                 bottom = userptr_add ( bottom, len );
91                 heap_size += len;
92         }
93 }
94
95 /**
96  * Reallocate external memory
97  *
98  * @v old_ptr           Memory previously allocated by umalloc(), or UNULL
99  * @v new_size          Requested size
100  * @ret new_ptr         Allocated memory, or UNULL
101  *
102  * Calling realloc() with a new size of zero is a valid way to free a
103  * memory block.
104  */
105 static userptr_t memtop_urealloc ( userptr_t ptr, size_t new_size ) {
106         struct external_memory extmem;
107         userptr_t new = ptr;
108         size_t align;
109
110         /* (Re)initialise external memory allocator if necessary */
111         if ( bottom == top )
112                 init_eheap();
113
114         /* Get block properties into extmem */
115         if ( ptr && ( ptr != UNOWHERE ) ) {
116                 /* Determine old size */
117                 copy_from_user ( &extmem, ptr, -sizeof ( extmem ),
118                                  sizeof ( extmem ) );
119         } else {
120                 /* Create a zero-length block */
121                 if ( heap_size < sizeof ( extmem ) ) {
122                         DBG ( "EXTMEM out of space\n" );
123                         return UNULL;
124                 }
125                 ptr = bottom = userptr_add ( bottom, -sizeof ( extmem ) );
126                 heap_size -= sizeof ( extmem );
127                 DBG ( "EXTMEM allocating [%lx,%lx)\n",
128                       user_to_phys ( ptr, 0 ), user_to_phys ( ptr, 0 ) );
129                 extmem.size = 0;
130         }
131         extmem.used = ( new_size > 0 );
132
133         /* Expand/shrink block if possible */
134         if ( ptr == bottom ) {
135                 /* Update block */
136                 if ( new_size > ( heap_size - extmem.size ) ) {
137                         DBG ( "EXTMEM out of space\n" );
138                         return UNULL;
139                 }
140                 new = userptr_add ( ptr, - ( new_size - extmem.size ) );
141                 align = ( user_to_phys ( new, 0 ) & ( EM_ALIGN - 1 ) );
142                 new_size += align;
143                 new = userptr_add ( new, -align );
144                 DBG ( "EXTMEM expanding [%lx,%lx) to [%lx,%lx)\n",
145                       user_to_phys ( ptr, 0 ),
146                       user_to_phys ( ptr, extmem.size ),
147                       user_to_phys ( new, 0 ),
148                       user_to_phys ( new, new_size ));
149                 memmove_user ( new, 0, ptr, 0, ( ( extmem.size < new_size ) ?
150                                                  extmem.size : new_size ) );
151                 bottom = new;
152                 heap_size -= ( new_size - extmem.size );
153                 extmem.size = new_size;
154         } else {
155                 /* Cannot expand; can only pretend to shrink */
156                 if ( new_size > extmem.size ) {
157                         /* Refuse to expand */
158                         DBG ( "EXTMEM cannot expand [%lx,%lx)\n",
159                               user_to_phys ( ptr, 0 ),
160                               user_to_phys ( ptr, extmem.size ) );
161                         return UNULL;
162                 }
163         }
164
165         /* Write back block properties */
166         copy_to_user ( new, -sizeof ( extmem ), &extmem,
167                        sizeof ( extmem ) );
168
169         /* Collect any free blocks and update hidden memory region */
170         ecollect_free();
171         hide_umalloc ( user_to_phys ( bottom, ( ( bottom == top ) ?
172                                                 0 : -sizeof ( extmem ) ) ),
173                        user_to_phys ( top, 0 ) );
174
175         return ( new_size ? new : UNOWHERE );
176 }
177
178 PROVIDE_UMALLOC ( memtop, urealloc, memtop_urealloc );