11
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
// See the License for the specific language governing permissions and
13
13
// limitations under the License.
14
+ use atomic_refcell:: AtomicRefCell ;
14
15
15
- use crate :: fat:: { self , Read } ;
16
+ use crate :: {
17
+ boot:: { E820Entry , Header , Info , Params } ,
18
+ fat:: { self , Read } ,
19
+ mem:: MemoryRegion ,
20
+ } ;
16
21
17
22
#[ derive( Debug ) ]
18
23
pub enum Error {
19
24
FileError ( fat:: Error ) ,
20
- KernelOld ,
25
+ NoInitrdMemory ,
21
26
MagicMissing ,
22
27
NotRelocatable ,
23
28
}
@@ -28,185 +33,145 @@ impl From<fat::Error> for Error {
28
33
}
29
34
}
30
35
31
- // From firecracker
32
- /// Kernel command line start address.
33
- const CMDLINE_START : usize = 0x4b000 ;
34
- /// Kernel command line start address maximum size.
35
- const CMDLINE_MAX_SIZE : usize = 0x10000 ;
36
- /// The 'zero page', a.k.a linux kernel bootparams.
37
- pub const ZERO_PAGE_START : usize = 0x7000 ;
36
+ const KERNEL_LOCATION : u64 = 0x20_0000 ;
38
37
39
- const KERNEL_LOCATION : u32 = 0x20_0000 ;
38
+ #[ repr( transparent) ]
39
+ pub struct Kernel ( Params ) ;
40
40
41
- const E820_RAM : u32 = 1 ;
42
-
43
- #[ repr( C , packed) ]
44
- struct E820Entry {
45
- addr : u64 ,
46
- size : u64 ,
47
- entry_type : u32 ,
48
- }
49
-
50
- pub fn load_initrd ( f : & mut dyn Read ) -> Result < ( ) , Error > {
51
- let mut zero_page = crate :: mem:: MemoryRegion :: new ( ZERO_PAGE_START as u64 , 4096 ) ;
52
-
53
- let mut max_load_address = u64:: from ( zero_page. read_u32 ( 0x22c ) ) ;
54
- if max_load_address == 0 {
55
- max_load_address = 0x37ff_ffff ;
41
+ impl Kernel {
42
+ pub fn new ( info : & dyn Info ) -> Self {
43
+ let mut kernel = Self ( Params :: new ( ) ) ;
44
+ kernel. 0 . acpi_rsdp_addr = info. rsdp_addr ( ) ;
45
+ kernel. 0 . set_entries ( info) ;
46
+ kernel
56
47
}
57
48
58
- let e820_count = zero_page . read_u8 ( 0x1e8 ) ;
59
- let e820_table = zero_page . as_mut_slice :: < E820Entry > ( 0x2d0 , u64 :: from ( e820_count ) ) ;
49
+ pub fn load_kernel ( & mut self , f : & mut dyn Read ) -> Result < ( ) , Error > {
50
+ self . 0 . hdr = Header :: from_file ( f ) ? ;
60
51
61
- // Search E820 table for highest usable ram location that is below the limit.
62
- let mut top_of_usable_ram = 0 ;
63
- for entry in e820_table {
64
- if entry. entry_type == E820_RAM {
65
- let m = entry. addr + entry. size - 1 ;
66
- if m > top_of_usable_ram && m < max_load_address {
67
- top_of_usable_ram = m;
68
- }
52
+ if self . 0 . hdr . boot_flag != 0xAA55 || self . 0 . hdr . header != * b"HdrS" {
53
+ return Err ( Error :: MagicMissing ) ;
54
+ }
55
+ // Check relocatable
56
+ if self . 0 . hdr . version < 0x205 || self . 0 . hdr . relocatable_kernel == 0 {
57
+ return Err ( Error :: NotRelocatable ) ;
69
58
}
70
- }
71
59
72
- if top_of_usable_ram > max_load_address {
73
- top_of_usable_ram = max_load_address;
60
+ // Skip over the setup sectors
61
+ let setup_sects = match self . 0 . hdr . setup_sects {
62
+ 0 => 4 ,
63
+ n => n as u32 ,
64
+ } ;
65
+ let setup_bytes = ( setup_sects + 1 ) * 512 ;
66
+ let remaining_bytes = f. get_size ( ) - setup_bytes;
67
+
68
+ let mut region = MemoryRegion :: new ( KERNEL_LOCATION , remaining_bytes as u64 ) ;
69
+ f. seek ( setup_bytes) ?;
70
+ f. load_file ( & mut region) ?;
71
+
72
+ // Fill out "write/modify" fields
73
+ self . 0 . hdr . type_of_loader = 0xff ; // Unknown Loader
74
+ self . 0 . hdr . code32_start = KERNEL_LOCATION as u32 ; // Where we load the kernel
75
+ self . 0 . hdr . cmd_line_ptr = CMDLINE_START as u32 ; // Where we load the cmdline
76
+ Ok ( ( ) )
74
77
}
75
78
76
- // Align address to 2MiB boundary as we use 2 MiB pages
77
- let initrd_address = ( top_of_usable_ram - u64:: from ( f. get_size ( ) ) ) & !( ( 2 << 20 ) - 1 ) ;
78
- let mut initrd_region = crate :: mem:: MemoryRegion :: new ( initrd_address, u64:: from ( f. get_size ( ) ) ) ;
79
-
80
- let mut offset = 0 ;
81
- while offset < f. get_size ( ) {
82
- let bytes_remaining = f. get_size ( ) - offset;
83
-
84
- // Use intermediata buffer for last, partial sector
85
- if bytes_remaining < 512 {
86
- let mut data: [ u8 ; 512 ] = [ 0 ; 512 ] ;
87
- match f. read ( & mut data) {
88
- Err ( crate :: fat:: Error :: EndOfFile ) => break ,
89
- Err ( e) => return Err ( Error :: FileError ( e) ) ,
90
- Ok ( _) => { }
91
- }
92
- let dst = initrd_region. as_mut_slice ( u64:: from ( offset) , u64:: from ( bytes_remaining) ) ;
93
- dst. copy_from_slice ( & data[ 0 ..bytes_remaining as usize ] ) ;
94
- break ;
95
- }
79
+ // Compute the load address for the initial ramdisk
80
+ fn initrd_addr ( & self , size : u64 ) -> Option < u64 > {
81
+ let initrd_addr_max = match self . 0 . hdr . initrd_addr_max {
82
+ 0 => 0x37FFFFFF ,
83
+ a => a as u64 ,
84
+ } ;
85
+ let max_start = ( initrd_addr_max + 1 ) - size;
96
86
97
- let dst = initrd_region. as_mut_slice ( u64:: from ( offset) , 512 ) ;
98
- match f. read ( dst) {
99
- Err ( crate :: fat:: Error :: EndOfFile ) => break ,
100
- Err ( e) => return Err ( Error :: FileError ( e) ) ,
101
- Ok ( _) => { }
87
+ let mut option_addr = None ;
88
+ for i in 0 ..self . 0 . num_entries ( ) {
89
+ let entry = self . 0 . entry ( i) ;
90
+ if entry. entry_type != E820Entry :: RAM_TYPE {
91
+ continue ;
92
+ }
93
+ let addr = entry. addr + entry. size - size;
94
+ // Align address to 2MiB boundary as we use 2 MiB pages
95
+ let addr = addr & !( ( 2 << 20 ) - 1 ) ;
96
+ // The ramdisk must fit in the region completely
97
+ if addr > max_start || addr < entry. addr {
98
+ continue ;
99
+ }
100
+ // Use the largest address we can find
101
+ if let Some ( load_addr) = option_addr {
102
+ if load_addr >= addr {
103
+ continue ;
104
+ }
105
+ }
106
+ option_addr = Some ( addr)
102
107
}
103
-
104
- offset += 512 ;
108
+ option_addr
105
109
}
106
110
107
- // initrd pointer/size
108
- zero_page. write_u32 ( 0x218 , initrd_address as u32 ) ;
109
- zero_page. write_u32 ( 0x21c , f. get_size ( ) ) ;
110
- Ok ( ( ) )
111
- }
112
-
113
- pub fn append_commandline ( addition : & str ) -> Result < ( ) , Error > {
114
- let mut cmdline_region =
115
- crate :: mem:: MemoryRegion :: new ( CMDLINE_START as u64 , CMDLINE_MAX_SIZE as u64 ) ;
116
- let zero_page = crate :: mem:: MemoryRegion :: new ( ZERO_PAGE_START as u64 , 4096 ) ;
117
-
118
- let cmdline = cmdline_region. as_mut_slice :: < u8 > ( 0 , CMDLINE_MAX_SIZE as u64 ) ;
119
-
120
- // Use the actual string length but limit to the orgiginal incoming size
121
- let orig_len = zero_page. read_u32 ( 0x238 ) as usize ;
122
-
123
- let orig_cmdline = unsafe {
124
- core:: str:: from_utf8_unchecked ( & cmdline[ 0 ..orig_len] ) . trim_matches ( char:: from ( 0 ) )
125
- } ;
126
- let orig_len = orig_cmdline. len ( ) ;
127
-
128
- cmdline[ orig_len] = b' ' ;
129
- cmdline[ orig_len + 1 ..orig_len + 1 + addition. len ( ) ] . copy_from_slice ( addition. as_bytes ( ) ) ;
130
- cmdline[ orig_len + 1 + addition. len ( ) ] = 0 ;
131
-
132
- // Commandline pointer/size
133
- zero_page. write_u32 ( 0x228 , CMDLINE_START as u32 ) ;
134
- zero_page. write_u32 ( 0x238 , ( orig_len + addition. len ( ) + 1 ) as u32 ) ;
135
-
136
- Ok ( ( ) )
137
- }
138
-
139
- pub fn load_kernel ( f : & mut dyn Read ) -> Result < u64 , Error > {
140
- f. seek ( 0 ) ?;
141
-
142
- let mut buf: [ u8 ; 1024 ] = [ 0 ; 1024 ] ;
143
-
144
- f. read ( & mut buf[ 0 ..512 ] ) ?;
145
- f. read ( & mut buf[ 512 ..] ) ?;
146
-
147
- let setup = crate :: mem:: MemoryRegion :: from_bytes ( & mut buf[ ..] ) ;
111
+ pub fn load_initrd ( & mut self , f : & mut dyn Read ) -> Result < ( ) , Error > {
112
+ let size = f. get_size ( ) as u64 ;
113
+ let addr = match self . initrd_addr ( size) {
114
+ Some ( addr) => addr,
115
+ None => return Err ( Error :: NoInitrdMemory ) ,
116
+ } ;
148
117
149
- if setup . read_u16 ( 0x1fe ) != 0xAA55 {
150
- return Err ( Error :: MagicMissing ) ;
151
- }
118
+ let mut region = MemoryRegion :: new ( addr , size ) ;
119
+ f . seek ( 0 ) ? ;
120
+ f . load_file ( & mut region ) ? ;
152
121
153
- if setup. read_u32 ( 0x202 ) != 0x5372_6448 {
154
- return Err ( Error :: MagicMissing ) ;
122
+ // initrd pointer/size
123
+ self . 0 . hdr . ramdisk_image = addr as u32 ;
124
+ self . 0 . hdr . ramdisk_size = size as u32 ;
125
+ Ok ( ( ) )
155
126
}
156
127
157
- // Need for relocation
158
- if setup. read_u16 ( 0x206 ) < 0x205 {
159
- return Err ( Error :: KernelOld ) ;
128
+ pub fn append_cmdline ( & mut self , addition : & [ u8 ] ) {
129
+ if !addition. is_empty ( ) {
130
+ CMDLINE . borrow_mut ( ) . append ( addition) ;
131
+ assert ! ( CMDLINE . borrow( ) . len( ) < self . 0 . hdr. cmdline_size) ;
132
+ }
160
133
}
161
134
162
- // Check relocatable
163
- if setup. read_u8 ( 0x234 ) == 0 {
164
- return Err ( Error :: NotRelocatable ) ;
135
+ pub fn boot ( & mut self ) {
136
+ // 0x200 is the startup_64 offset
137
+ let jump_address = self . 0 . hdr . code32_start as u64 + 0x200 ;
138
+ // Rely on x86 C calling convention where second argument is put into %rsi register
139
+ let ptr = jump_address as * const ( ) ;
140
+ let code: extern "C" fn ( usize , usize ) = unsafe { core:: mem:: transmute ( ptr) } ;
141
+ ( code) ( 0 /* dummy value */ , & mut self . 0 as * mut _ as usize ) ;
165
142
}
143
+ }
166
144
167
- let header_start = 0x1f1 as usize ;
168
- let header_end = 0x202 + buf[ 0x0201 ] as usize ;
169
-
170
- // Reuse the zero page that we were originally given
171
- // TODO: Zero and fill it ourself but will need to save E820 details
172
- let mut zero_page = crate :: mem:: MemoryRegion :: new ( ZERO_PAGE_START as u64 , 4096 ) ;
173
-
174
- let dst = zero_page. as_mut_slice ( header_start as u64 , ( header_end - header_start) as u64 ) ;
175
- dst. copy_from_slice ( & buf[ header_start..header_end] ) ;
176
-
177
- // Unknown loader
178
- zero_page. write_u8 ( 0x210 , 0xff ) ;
145
+ // This is the highest region at which we can load the kernel command line.
146
+ const CMDLINE_START : u64 = 0x4b000 ;
147
+ const CMDLINE_MAX_LEN : u64 = 0x10000 ;
179
148
180
- // Where we will load the kernel into
181
- zero_page. write_u32 ( 0x214 , KERNEL_LOCATION ) ;
149
+ static CMDLINE : AtomicRefCell < CmdLine > = AtomicRefCell :: new ( CmdLine :: new ( ) ) ;
182
150
183
- let mut setup_sects = buf[ header_start] as usize ;
151
+ struct CmdLine {
152
+ region : MemoryRegion ,
153
+ length : usize , // Does not include null pointer
154
+ }
184
155
185
- if setup_sects == 0 {
186
- setup_sects = 4 ;
156
+ impl CmdLine {
157
+ const fn new ( ) -> Self {
158
+ Self {
159
+ region : MemoryRegion :: new ( CMDLINE_START , CMDLINE_MAX_LEN ) ,
160
+ length : 0 ,
161
+ }
187
162
}
188
163
189
- setup_sects += 1 ; // Include the boot sector
190
-
191
- let setup_bytes = setup_sects * 512 ; // Use to start reading the main image
192
-
193
- let mut load_offset = u64:: from ( KERNEL_LOCATION ) ;
194
-
195
- f. seek ( setup_bytes as u32 ) ?;
196
-
197
- loop {
198
- let mut dst = crate :: mem:: MemoryRegion :: new ( load_offset, 512 ) ;
199
- let dst = dst. as_mut_slice ( 0 , 512 ) ;
164
+ const fn len ( & self ) -> u32 {
165
+ self . length as u32
166
+ }
200
167
201
- match f. read ( dst) {
202
- Err ( crate :: fat:: Error :: EndOfFile ) => {
203
- // 0x200 is the startup_64 offset
204
- return Ok ( u64:: from ( KERNEL_LOCATION ) + 0x200 ) ;
205
- }
206
- Err ( e) => return Err ( Error :: FileError ( e) ) ,
207
- Ok ( _) => { }
208
- } ;
168
+ fn append ( & mut self , args : & [ u8 ] ) {
169
+ let bytes = self . region . as_bytes ( ) ;
170
+ bytes[ self . length ] = b' ' ;
171
+ self . length += 1 ;
209
172
210
- load_offset += 512 ;
173
+ bytes[ self . length ..self . length + args. len ( ) ] . copy_from_slice ( args) ;
174
+ self . length += args. len ( ) ;
175
+ bytes[ self . length ] = 0 ;
211
176
}
212
177
}
0 commit comments