1
- use x86_64:: PhysAddr ;
2
1
use x86_64:: structures:: paging:: { PAGE_SIZE , PhysFrame } ;
3
2
use os_bootinfo:: { MemoryMap , MemoryRegion , MemoryRegionType } ;
4
3
@@ -7,97 +6,131 @@ pub(crate) struct FrameAllocator<'a> {
7
6
}
8
7
9
8
impl < ' a > FrameAllocator < ' a > {
10
- pub ( crate ) fn allocate_frame ( & mut self ) -> PhysFrame {
9
+ pub ( crate ) fn allocate_frame ( & mut self , region_type : MemoryRegionType ) -> Option < PhysFrame > {
11
10
let page_size = u64:: from ( PAGE_SIZE ) ;
12
11
let mut frame = None ;
13
12
for region in self . memory_map . iter_mut ( ) {
14
- if region. start_addr < PhysAddr :: new ( 1024 * 1024 ) {
15
- // don't allocate memory below 1M, since it might be used by bootloader or BIOS
16
- continue ;
17
- }
18
13
if region. region_type != MemoryRegionType :: Usable {
19
14
continue ;
20
15
}
21
16
if region. len < page_size {
22
17
continue ;
23
18
}
24
19
assert_eq ! ( 0 , region. start_addr. as_u64( ) & 0xfff ,
25
- "Region start address must be aligned" ) ;
20
+ "Region start address is not page aligned: {:?}" , region ) ;
26
21
27
22
frame = Some ( PhysFrame :: containing_address ( region. start_addr ) ) ;
28
23
region. start_addr += page_size;
24
+ region. len -= page_size;
29
25
break ;
30
26
}
31
27
if let Some ( frame) = frame {
32
- self . mark_as_used ( frame. start_address ( ) , page_size) ;
33
- frame
28
+ self . add_region ( MemoryRegion {
29
+ start_addr : frame. start_address ( ) ,
30
+ len : page_size,
31
+ region_type
32
+ } ) ;
33
+ Some ( frame)
34
34
} else {
35
- panic ! ( "Out of physical memory" ) ;
35
+ None
36
36
}
37
37
}
38
38
39
- pub ( crate ) fn mark_as_used ( & mut self , addr : PhysAddr , len : u64 ) {
40
- let mut used_region = Some ( MemoryRegion {
41
- start_addr : addr,
42
- len,
43
- region_type : MemoryRegionType :: InUse ,
44
- } ) ;
45
- let mut new_region = None ;
39
+ pub ( crate ) fn deallocate_frame ( & mut self , frame : PhysFrame ) {
40
+ let page_size = u64:: from ( PAGE_SIZE ) ;
41
+ self . add_region_overwrite ( MemoryRegion {
42
+ start_addr : frame. start_address ( ) ,
43
+ len : page_size,
44
+ region_type : MemoryRegionType :: Usable ,
45
+ } , true ) ;
46
+ }
46
47
47
- // check if it overlaps with another region
48
- for region in self . memory_map . iter_mut ( ) {
49
- let region_start = region. start_addr ;
50
- let region_end = region. start_addr + region. len ;
51
- let used_start = addr;
52
- let used_end = addr + len;
48
+ /// Adds the passed region to the memory map.
49
+ ///
50
+ /// This function automatically adjusts the existing regions so that no overlap occurs.
51
+ ///
52
+ /// Panics if a non-usable region (e.g. a reserved region) overlaps with the passed region.
53
+ pub ( crate ) fn add_region ( & mut self , region : MemoryRegion )
54
+ {
55
+ self . add_region_overwrite ( region, false ) ;
56
+ }
57
+
58
+ fn add_region_overwrite ( & mut self , region : MemoryRegion , overwrite : bool ) {
59
+ assert_eq ! ( 0 , region. start_addr. as_u64( ) & 0xfff ,
60
+ "Region start address is not page aligned: {:?}" , region) ;
53
61
54
- if region_start < used_end && region_end > used_start {
55
- // used area overlaps with region
56
- assert ! ( region. region_type == MemoryRegionType :: Usable ) ;
57
- if region_start < used_start && region_end > used_end {
58
- // Case: (R = region, U = used_area)
59
- // ----RRRRRRRRRRR----
60
- // ------UUUU---------
61
- region. len = used_start - region_start;
62
- assert ! ( new_region. is_none( ) , "area overlaps with multiple regions" ) ;
63
- new_region = Some ( MemoryRegion {
64
- start_addr : used_end,
65
- len : region_end - used_end,
66
- region_type : MemoryRegionType :: Usable ,
62
+ let mut region_already_inserted = false ;
63
+ let mut split_region = None ;
64
+
65
+ for r in self . memory_map . iter_mut ( ) {
66
+ // check if region overlaps with another region
67
+ if r. start_addr ( ) < region. end_addr ( ) && r. end_addr ( ) > region. start_addr ( ) {
68
+ // region overlaps with `r`
69
+ match r. region_type {
70
+ MemoryRegionType :: Usable => {
71
+ if region. region_type == MemoryRegionType :: Usable {
72
+ panic ! ( "region {:?} overlaps with other usable region {:?}" , region, r)
73
+ }
74
+ }
75
+ MemoryRegionType :: InUse => { } ,
76
+ MemoryRegionType :: Bootloader | MemoryRegionType :: Kernel
77
+ | MemoryRegionType :: PageTable if overwrite => { }
78
+ _ => {
79
+ panic ! ( "can't override region {:?} with {:?}" , r, region) ;
80
+ }
81
+ }
82
+ if r. start_addr ( ) < region. start_addr ( ) && r. end_addr ( ) > region. end_addr ( ) {
83
+ // Case: (r = `r`, R = `region`)
84
+ // ----rrrrrrrrrrr----
85
+ // ------RRRR---------
86
+ r. len = region. start_addr ( ) - r. start_addr ( ) ;
87
+ assert ! ( split_region. is_none( ) , "area overlaps with multiple regions" ) ;
88
+ split_region = Some ( MemoryRegion {
89
+ start_addr : region. end_addr ( ) ,
90
+ len : r. end_addr ( ) - region. end_addr ( ) ,
91
+ region_type : r. region_type ,
67
92
} ) ;
68
- } else if used_start <= region_start {
69
- // Case: (R = region, U = used_area)
70
- // ----RRRRRRRRRRR----
71
- // --UUUU-------------
72
- region. start_addr = used_end;
73
- } else if used_end >= used_end {
74
- // Case: (R = region, U = used_area)
75
- // ----RRRRRRRRRRR----
76
- // -------------UUUU--
77
- region. len = used_start - region_start;
93
+ } else if region. start_addr ( ) <= r. start_addr ( ) {
94
+ // Case: (r = `r`, R = `region`)
95
+ // ----rrrrrrrrrrr----
96
+ // --RRRR-------------
97
+ r. len -= region. end_addr ( ) - r. start_addr ( ) ;
98
+ r. start_addr = region. end_addr ( ) ;
99
+ } else if region. end_addr ( ) >= r. end_addr ( ) {
100
+ // Case: (r = `r`, R = `region`)
101
+ // ----rrrrrrrrrrr----
102
+ // -------------RRRR--
103
+ r. len = region. start_addr ( ) - r. start_addr ( ) ;
104
+ } else {
105
+ unreachable ! ( "region overlaps in an unexpected way" )
78
106
}
79
107
}
80
- if region. region_type == MemoryRegionType :: InUse {
81
- if used_end == region_start {
82
- // merge regions
83
- if let Some ( used_region) = used_region. take ( ) {
84
- region. start_addr = used_region. start_addr ;
85
- region. len += used_region. len ;
86
- }
87
- } else if region_end == used_start {
88
- // merge regions
89
- if let Some ( used_region) = used_region. take ( ) {
90
- region. len += used_region. len ;
91
- }
108
+ // check if region is adjacent to already existing region (only if same type)
109
+ if r. region_type == region. region_type {
110
+ if region. end_addr ( ) == r. start_addr ( ) {
111
+ // Case: (r = `r`, R = `region`)
112
+ // ------rrrrrrrrrrr--
113
+ // --RRRR-------------
114
+ // => merge regions
115
+ r. start_addr = region. start_addr ( ) ;
116
+ r. len += region. len ;
117
+ region_already_inserted = true ;
118
+ } else if region. start_addr ( ) == r. end_addr ( ) {
119
+ // Case: (r = `r`, R = `region`)
120
+ // --rrrrrrrrrrr------
121
+ // -------------RRRR--
122
+ // => merge regions
123
+ r. len += region. len ;
124
+ region_already_inserted = true ;
92
125
}
93
126
}
94
127
}
95
128
96
- if let Some ( new_region ) = new_region {
97
- self . memory_map . add_region ( new_region ) ;
129
+ if let Some ( split_region ) = split_region {
130
+ self . memory_map . add_region ( split_region ) ;
98
131
}
99
- if let Some ( used_region ) = used_region {
100
- self . memory_map . add_region ( used_region ) ;
132
+ if !region_already_inserted {
133
+ self . memory_map . add_region ( region ) ;
101
134
}
102
135
}
103
136
}
0 commit comments