summaryrefslogtreecommitdiffstats
path: root/src/core/memmap.c
blob: 4a96055e6e6ddbf9db7e1932e7567adf7d4f24c0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/*
 * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 *
 * You can also choose to distribute this program under the terms of
 * the Unmodified Binary Distribution Licence (as given in the file
 * COPYING.UBDL), provided that you have satisfied its requirements.
 */

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );

#include <assert.h>
#include <ipxe/io.h>
#include <ipxe/memmap.h>

/** @file
 *
 * System memory map
 *
 */

/**
 * Update memory region descriptor
 *
 * @v region		Memory region of interest to be updated
 * @v start		Start address of known region
 * @v size		Size of known region
 * @v flags		Flags for known region
 * @v name		Name of known region (for debugging)
 *
 * Update a memory region descriptor based on a known existent region.
 */
void memmap_update ( struct memmap_region *region, uint64_t start,
		     uint64_t size, unsigned int flags, const char *name ) {
	uint64_t max;

	/* Sanity check */
	assert ( region->max >= region->min );

	/* Ignore empty regions */
	if ( ! size )
		return;

	/* Calculate max addresses (and truncate if necessary) */
	max = ( start + size - 1 );
	if ( max < start ) {
		max = ~( ( uint64_t ) 0 );
		DBGC ( region, "MEMMAP [%#08llx,%#08llx] %s truncated "
		       "(invalid size %#08llx)\n",
		       ( ( unsigned long long ) start ),
		       ( ( unsigned long long ) max ), name,
		       ( ( unsigned long long ) size ) );
	}

	/* Ignore regions entirely below the region of interest */
	if ( max < region->min )
		return;

	/* Ignore regions entirely above the region of interest */
	if ( start > region->max )
		return;

	/* Update region of interest as applicable */
	if ( start <= region->min ) {

		/* Record this region as covering the region of interest */
		region->flags |= flags;
		if ( name )
			region->name = name;

		/* Update max address if no closer boundary exists */
		if ( max < region->max )
			region->max = max;

	} else if ( start < region->max ) {

		/* Update max address if no closer boundary exists */
		region->max = ( start - 1 );
	}

	/* Sanity check */
	assert ( region->max >= region->min );
}

/**
 * Update memory region descriptor based on all in-use memory regions
 *
 * @v region		Memory region of interest to be updated
 */
void memmap_update_used ( struct memmap_region *region ) {
	struct used_region *used;

	/* Update descriptor to hide all in-use regions */
	for_each_table_entry ( used, USED_REGIONS ) {
		memmap_update ( region, used->start, used->size,
				MEMMAP_FL_USED, used->name );
	}
}

/**
 * Find largest usable memory region
 *
 * @v start		Start address to fill in
 * @ret len		Length of region
 */
size_t memmap_largest ( physaddr_t *start ) {
	struct memmap_region region;
	size_t largest;
	size_t size;

	/* Find largest usable region */
	DBGC ( &region, "MEMMAP finding largest usable region\n" );
	*start = 0;
	largest = 0;
	for_each_memmap ( &region, 1 ) {
		DBGC_MEMMAP ( &region, &region );
		if ( ! memmap_is_usable ( &region ) )
			continue;
		size = memmap_size ( &region );
		if ( size > largest ) {
			DBGC ( &region, "...new largest region found\n" );
			largest = size;
			*start = region.min;
		}
	}
	return largest;
}

PROVIDE_MEMMAP_INLINE ( null, memmap_describe );
PROVIDE_MEMMAP_INLINE ( null, memmap_sync );