1111// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212// See the License for the specific language governing permissions and
1313// limitations under the License.
14+ use atomic_refcell:: AtomicRefCell ;
1415
15- use crate :: fat:: { self , Read } ;
16+ use crate :: {
17+ boot:: { E820Entry , Header , Info , Params } ,
18+ fat:: { self , Read } ,
19+ mem:: MemoryRegion ,
20+ } ;
1621
1722#[ derive( Debug ) ]
1823pub enum Error {
1924 FileError ( fat:: Error ) ,
20- KernelOld ,
25+ NoInitrdMemory ,
2126 MagicMissing ,
2227 NotRelocatable ,
2328}
@@ -28,185 +33,145 @@ impl From<fat::Error> for Error {
2833 }
2934}
3035
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 ;
3837
39- const KERNEL_LOCATION : u32 = 0x20_0000 ;
38+ #[ repr( transparent) ]
39+ pub struct Kernel ( Params ) ;
4040
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
5647 }
5748
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 ) ? ;
6051
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 ) ;
6958 }
70- }
7159
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 ( ( ) )
7477 }
7578
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 => 0x37FF_FFFF ,
83+ a => a as u64 ,
84+ } ;
85+ let max_start = ( initrd_addr_max + 1 ) - size;
9686
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)
102107 }
103-
104- offset += 512 ;
108+ option_addr
105109 }
106110
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+ } ;
148117
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 ) ? ;
152121
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 ( ( ) )
155126 }
156127
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+ }
160133 }
161134
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 ) ;
165142 }
143+ }
166144
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 ;
179148
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 ( ) ) ;
182150
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+ }
184155
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+ }
187162 }
188163
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+ }
200167
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 ;
209172
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 ;
211176 }
212177}
0 commit comments