1 | /* |
---|
2 | * Copyright (C) 2008 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 | FILE_LICENCE ( GPL2_OR_LATER ); |
---|
20 | |
---|
21 | #include <assert.h> |
---|
22 | #include <gpxe/umalloc.h> |
---|
23 | #include <gpxe/efi/efi.h> |
---|
24 | |
---|
25 | /** @file |
---|
26 | * |
---|
27 | * gPXE user memory allocation API for EFI |
---|
28 | * |
---|
29 | */ |
---|
30 | |
---|
31 | /** Equivalent of NOWHERE for user pointers */ |
---|
32 | #define UNOWHERE ( ~UNULL ) |
---|
33 | |
---|
34 | /** |
---|
35 | * Reallocate external memory |
---|
36 | * |
---|
37 | * @v old_ptr Memory previously allocated by umalloc(), or UNULL |
---|
38 | * @v new_size Requested size |
---|
39 | * @ret new_ptr Allocated memory, or UNULL |
---|
40 | * |
---|
41 | * Calling realloc() with a new size of zero is a valid way to free a |
---|
42 | * memory block. |
---|
43 | */ |
---|
44 | static userptr_t efi_urealloc ( userptr_t old_ptr, size_t new_size ) { |
---|
45 | EFI_BOOT_SERVICES *bs = efi_systab->BootServices; |
---|
46 | EFI_PHYSICAL_ADDRESS phys_addr; |
---|
47 | unsigned int new_pages, old_pages; |
---|
48 | userptr_t new_ptr = UNOWHERE; |
---|
49 | size_t old_size; |
---|
50 | EFI_STATUS efirc; |
---|
51 | |
---|
52 | /* Allocate new memory if necessary. If allocation fails, |
---|
53 | * return without touching the old block. |
---|
54 | */ |
---|
55 | if ( new_size ) { |
---|
56 | new_pages = ( EFI_SIZE_TO_PAGES ( new_size ) + 1 ); |
---|
57 | if ( ( efirc = bs->AllocatePages ( AllocateAnyPages, |
---|
58 | EfiBootServicesData, |
---|
59 | new_pages, |
---|
60 | &phys_addr ) ) != 0 ) { |
---|
61 | DBG ( "EFI could not allocate %d pages: %s\n", |
---|
62 | new_pages, efi_strerror ( efirc ) ); |
---|
63 | return UNULL; |
---|
64 | } |
---|
65 | assert ( phys_addr != 0 ); |
---|
66 | new_ptr = phys_to_user ( phys_addr + EFI_PAGE_SIZE ); |
---|
67 | copy_to_user ( new_ptr, -EFI_PAGE_SIZE, |
---|
68 | &new_size, sizeof ( new_size ) ); |
---|
69 | DBG ( "EFI allocated %d pages at %llx\n", |
---|
70 | new_pages, phys_addr ); |
---|
71 | } |
---|
72 | |
---|
73 | /* Copy across relevant part of the old data region (if any), |
---|
74 | * then free it. Note that at this point either (a) new_ptr |
---|
75 | * is valid, or (b) new_size is 0; either way, the memcpy() is |
---|
76 | * valid. |
---|
77 | */ |
---|
78 | if ( old_ptr && ( old_ptr != UNOWHERE ) ) { |
---|
79 | copy_from_user ( &old_size, old_ptr, -EFI_PAGE_SIZE, |
---|
80 | sizeof ( old_size ) ); |
---|
81 | memcpy_user ( new_ptr, 0, old_ptr, 0, |
---|
82 | ( (old_size < new_size) ? old_size : new_size )); |
---|
83 | old_pages = ( EFI_SIZE_TO_PAGES ( old_size ) + 1 ); |
---|
84 | phys_addr = user_to_phys ( old_ptr, -EFI_PAGE_SIZE ); |
---|
85 | if ( ( efirc = bs->FreePages ( phys_addr, old_pages ) ) != 0 ){ |
---|
86 | DBG ( "EFI could not free %d pages at %llx: %s\n", |
---|
87 | old_pages, phys_addr, efi_strerror ( efirc ) ); |
---|
88 | /* Not fatal; we have leaked memory but successfully |
---|
89 | * allocated (if asked to do so). |
---|
90 | */ |
---|
91 | } |
---|
92 | DBG ( "EFI freed %d pages at %llx\n", old_pages, phys_addr ); |
---|
93 | } |
---|
94 | |
---|
95 | return new_ptr; |
---|
96 | } |
---|
97 | |
---|
98 | PROVIDE_UMALLOC ( efi, urealloc, efi_urealloc ); |
---|