2 * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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.
19 FILE_LICENCE ( GPL2_OR_LATER
);
24 * External memory allocation
30 #include <gpxe/uaccess.h>
31 #include <gpxe/hidemem.h>
32 #include <gpxe/memmap.h>
33 #include <gpxe/umalloc.h>
35 /** Alignment of external allocated memory */
36 #define EM_ALIGN ( 4 * 1024 )
38 /** Equivalent of NOWHERE for user pointers */
39 #define UNOWHERE ( ~UNULL )
41 /** An external memory block */
42 struct external_memory
{
43 /** Size of this memory block (excluding this header) */
45 /** Block is currently in use */
50 static userptr_t top
= UNULL
;
52 /** Bottom of heap (current lowest allocated block) */
53 static userptr_t bottom
= UNULL
;
56 * Initialise external heap
58 * @ret rc Return status code
60 static int init_eheap ( void ) {
61 struct memory_map memmap
;
62 unsigned long heap_size
= 0;
65 DBG ( "Allocating external heap\n" );
67 get_memmap ( &memmap
);
68 for ( i
= 0 ; i
< memmap
.count
; i
++ ) {
69 struct memory_region
*region
= &memmap
.regions
[i
];
70 unsigned long r_start
, r_end
;
73 DBG ( "Considering [%llx,%llx)\n", region
->start
, region
->end
);
75 /* Truncate block to 4GB */
76 if ( region
->start
> UINT_MAX
) {
77 DBG ( "...starts after 4GB\n" );
80 r_start
= region
->start
;
81 if ( region
->end
> UINT_MAX
) {
82 DBG ( "...end truncated to 4GB\n" );
83 r_end
= 0; /* =4GB, given the wraparound */
88 /* Use largest block */
89 r_size
= ( r_end
- r_start
);
90 if ( r_size
> heap_size
) {
91 DBG ( "...new best block found\n" );
92 top
= bottom
= phys_to_user ( r_end
);
98 DBG ( "No external heap available\n" );
102 DBG ( "External heap grows downwards from %lx\n",
103 user_to_phys ( top
, 0 ) );
108 * Collect free blocks
111 static void ecollect_free ( void ) {
112 struct external_memory extmem
;
114 /* Walk the free list and collect empty blocks */
115 while ( bottom
!= top
) {
116 copy_from_user ( &extmem
, bottom
, -sizeof ( extmem
),
120 DBG ( "EXTMEM freeing [%lx,%lx)\n", user_to_phys ( bottom
, 0 ),
121 user_to_phys ( bottom
, extmem
.size
) );
122 bottom
= userptr_add ( bottom
,
123 ( extmem
.size
+ sizeof ( extmem
) ) );
128 * Reallocate external memory
130 * @v old_ptr Memory previously allocated by umalloc(), or UNULL
131 * @v new_size Requested size
132 * @ret new_ptr Allocated memory, or UNULL
134 * Calling realloc() with a new size of zero is a valid way to free a
137 static userptr_t
memtop_urealloc ( userptr_t ptr
, size_t new_size
) {
138 struct external_memory extmem
;
143 /* Initialise external memory allocator if necessary */
144 if ( bottom
== top
) {
145 if ( ( rc
= init_eheap() ) != 0 )
149 /* Get block properties into extmem */
150 if ( ptr
&& ( ptr
!= UNOWHERE
) ) {
151 /* Determine old size */
152 copy_from_user ( &extmem
, ptr
, -sizeof ( extmem
),
155 /* Create a zero-length block */
156 ptr
= bottom
= userptr_add ( bottom
, -sizeof ( extmem
) );
157 DBG ( "EXTMEM allocating [%lx,%lx)\n",
158 user_to_phys ( ptr
, 0 ), user_to_phys ( ptr
, 0 ) );
161 extmem
.used
= ( new_size
> 0 );
163 /* Expand/shrink block if possible */
164 if ( ptr
== bottom
) {
166 new = userptr_add ( ptr
, - ( new_size
- extmem
.size
) );
167 align
= ( user_to_phys ( new, 0 ) & ( EM_ALIGN
- 1 ) );
169 new = userptr_add ( new, -align
);
170 DBG ( "EXTMEM expanding [%lx,%lx) to [%lx,%lx)\n",
171 user_to_phys ( ptr
, 0 ),
172 user_to_phys ( ptr
, extmem
.size
),
173 user_to_phys ( new, 0 ),
174 user_to_phys ( new, new_size
));
175 memmove_user ( new, 0, ptr
, 0, ( ( extmem
.size
< new_size
) ?
176 extmem
.size
: new_size
) );
177 extmem
.size
= new_size
;
180 /* Cannot expand; can only pretend to shrink */
181 if ( new_size
> extmem
.size
) {
182 /* Refuse to expand */
183 DBG ( "EXTMEM cannot expand [%lx,%lx)\n",
184 user_to_phys ( ptr
, 0 ),
185 user_to_phys ( ptr
, extmem
.size
) );
190 /* Write back block properties */
191 copy_to_user ( new, -sizeof ( extmem
), &extmem
,
194 /* Collect any free blocks and update hidden memory region */
196 hide_umalloc ( user_to_phys ( bottom
, -sizeof ( extmem
) ),
197 user_to_phys ( top
, 0 ) );
199 return ( new_size
? new : UNOWHERE
);
202 PROVIDE_UMALLOC ( memtop
, urealloc
, memtop_urealloc
);