Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/kernel_loader/src/arm64.rs
5394 views
1
// Copyright 2022 The ChromiumOS Authors
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
//! Linux arm64 kernel loader.
6
//! <https://www.kernel.org/doc/Documentation/arm64/booting.txt>
7
8
use std::cmp::max;
9
use std::io;
10
use std::io::BufRead;
11
use std::io::Read;
12
use std::io::Seek;
13
use std::io::SeekFrom;
14
use std::mem::size_of_val;
15
16
use base::warn;
17
use base::FileGetLen;
18
use base::FileReadWriteAtVolatile;
19
use base::VolatileSlice;
20
use data_model::Le32;
21
use data_model::Le64;
22
use lz4_flex::frame::FrameDecoder as Lz4FrameDecoder;
23
use resources::AddressRange;
24
use vm_memory::GuestAddress;
25
use vm_memory::GuestMemory;
26
use zerocopy::FromBytes;
27
use zerocopy::FromZeros;
28
use zerocopy::Immutable;
29
use zerocopy::IntoBytes;
30
use zerocopy::KnownLayout;
31
32
use crate::ElfClass;
33
use crate::Error;
34
use crate::LoadedKernel;
35
use crate::Result;
36
37
#[derive(Copy, Clone, FromBytes, Immutable, IntoBytes, KnownLayout)]
38
#[allow(unused)]
39
#[repr(C)]
40
struct Arm64ImageHeader {
41
code0: Le32,
42
code1: Le32,
43
text_offset: Le64,
44
image_size: Le64,
45
flags: Le64,
46
res2: Le64,
47
res3: Le64,
48
res4: Le64,
49
magic: Le32,
50
res5: Le32,
51
}
52
53
const ARM64_IMAGE_MAGIC: u32 = 0x644d5241; // "ARM\x64"
54
55
const ARM64_IMAGE_FLAG_BE_MASK: u64 = 0x1;
56
57
const ARM64_TEXT_OFFSET_DEFAULT: u64 = 0x80000;
58
59
impl Arm64ImageHeader {
60
fn parse_load_addr(&self, kernel_start: GuestAddress) -> Result<GuestAddress> {
61
let magic: u32 = self.magic.into();
62
if magic != ARM64_IMAGE_MAGIC {
63
return Err(Error::InvalidMagicNumber);
64
}
65
66
let flags: u64 = self.flags.into();
67
if flags & ARM64_IMAGE_FLAG_BE_MASK != 0 {
68
return Err(Error::BigEndianOnLittle);
69
}
70
71
let mut text_offset: u64 = self.text_offset.into();
72
let image_size: u64 = self.image_size.into();
73
74
if image_size == 0 {
75
warn!("arm64 Image header has an effective size of zero");
76
// arm64/booting.txt:
77
// "Where image_size is zero, text_offset can be assumed to be 0x80000."
78
text_offset = ARM64_TEXT_OFFSET_DEFAULT;
79
}
80
81
// Load the image into guest memory at `text_offset` bytes past `kernel_start`.
82
kernel_start
83
.checked_add(text_offset)
84
.ok_or(Error::InvalidKernelOffset)
85
}
86
}
87
88
pub fn load_arm64_kernel<F>(
89
guest_mem: &GuestMemory,
90
kernel_start: GuestAddress,
91
kernel_image: &mut F,
92
) -> Result<LoadedKernel>
93
where
94
F: FileReadWriteAtVolatile + FileGetLen,
95
{
96
let mut header = Arm64ImageHeader::new_zeroed();
97
kernel_image
98
.read_exact_at_volatile(VolatileSlice::new(header.as_mut_bytes()), 0)
99
.map_err(|_| Error::ReadHeader)?;
100
let load_addr = header.parse_load_addr(kernel_start)?;
101
102
let file_size = kernel_image.get_len().map_err(|_| Error::SeekKernelEnd)?;
103
let load_size = usize::try_from(file_size).map_err(|_| Error::InvalidKernelSize)?;
104
let range_size = max(file_size, u64::from(header.image_size));
105
106
let guest_slice = guest_mem
107
.get_slice_at_addr(load_addr, load_size)
108
.map_err(|_| Error::ReadKernelImage)?;
109
kernel_image
110
.read_exact_at_volatile(guest_slice, 0)
111
.map_err(|_| Error::ReadKernelImage)?;
112
113
Ok(LoadedKernel {
114
size: file_size,
115
address_range: AddressRange::from_start_and_size(load_addr.offset(), range_size)
116
.ok_or(Error::InvalidKernelSize)?,
117
entry: load_addr,
118
class: ElfClass::ElfClass64,
119
})
120
}
121
122
fn load_arm64_kernel_from_reader<F: BufRead>(
123
guest_mem: &GuestMemory,
124
kernel_start: GuestAddress,
125
mut kernel_image: F,
126
) -> Result<LoadedKernel> {
127
let mut header = Arm64ImageHeader::new_zeroed();
128
let header_size = u64::try_from(size_of_val(&header)).unwrap();
129
130
// Read and parse the kernel header.
131
kernel_image
132
.read_exact(header.as_mut_bytes())
133
.map_err(|_| Error::ReadHeader)?;
134
let load_addr = header.parse_load_addr(kernel_start)?;
135
136
// Write the parsed kernel header to memory. Avoid rewinding the reader back to the start.
137
guest_mem
138
.write_all_at_addr(header.as_bytes(), load_addr)
139
.map_err(|_| Error::ReadKernelImage)?;
140
141
// Continue reading from the source and copy the kernel image into GuestMemory.
142
let mut current_addr = load_addr
143
.checked_add(header_size)
144
.ok_or(Error::InvalidKernelSize)?;
145
loop {
146
let buf = match kernel_image.fill_buf() {
147
Ok([]) => break,
148
Ok(buf) => buf,
149
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => continue,
150
Err(_) => return Err(Error::ReadKernelImage),
151
};
152
153
guest_mem
154
.write_all_at_addr(buf, current_addr)
155
.map_err(|_| Error::ReadKernelImage)?;
156
157
let consumed = buf.len();
158
kernel_image.consume(consumed);
159
160
let offset = u64::try_from(consumed).map_err(|_| Error::InvalidKernelSize)?;
161
current_addr = current_addr
162
.checked_add(offset)
163
.ok_or(Error::InvalidKernelSize)?;
164
}
165
166
let file_size = current_addr.offset_from(load_addr);
167
let range_size = max(file_size, u64::from(header.image_size));
168
Ok(LoadedKernel {
169
size: file_size,
170
address_range: AddressRange::from_start_and_size(load_addr.offset(), range_size)
171
.ok_or(Error::InvalidKernelSize)?,
172
entry: load_addr,
173
class: ElfClass::ElfClass64,
174
})
175
}
176
177
pub fn load_arm64_kernel_lz4<F: Read + Seek>(
178
guest_mem: &GuestMemory,
179
kernel_start: GuestAddress,
180
mut kernel_image: F,
181
) -> Result<LoadedKernel> {
182
kernel_image
183
.seek(SeekFrom::Start(0))
184
.map_err(|_| Error::SeekKernelStart)?;
185
load_arm64_kernel_from_reader(
186
guest_mem,
187
kernel_start,
188
&mut Lz4FrameDecoder::new(kernel_image),
189
)
190
}
191
192
#[cfg(test)]
193
mod test {
194
use std::fs::File;
195
use std::io::Seek;
196
use std::io::SeekFrom;
197
use std::io::Write;
198
199
use tempfile::tempfile;
200
use vm_memory::GuestAddress;
201
use vm_memory::GuestMemory;
202
203
use crate::load_arm64_kernel;
204
use crate::load_arm64_kernel_lz4;
205
use crate::Error;
206
207
const MEM_SIZE: u64 = 0x200_0000;
208
209
fn create_guest_mem() -> GuestMemory {
210
GuestMemory::new(&[(GuestAddress(0x0), MEM_SIZE)]).unwrap()
211
}
212
213
#[allow(clippy::unusual_byte_groupings)]
214
fn write_valid_kernel() -> File {
215
let mut f = tempfile().expect("failed to create tempfile");
216
217
f.write_all(&[0x00, 0xC0, 0x2E, 0x14]).unwrap(); // code0
218
f.write_all(&[0x00, 0x00, 0x00, 0x00]).unwrap(); // code1
219
f.write_all(&0x00000000_00E70000u64.to_le_bytes()).unwrap(); // text_offset
220
f.write_all(&0x00000000_0000000Au64.to_le_bytes()).unwrap(); // image_size
221
f.write_all(&0x00000000_00000000u64.to_le_bytes()).unwrap(); // flags
222
f.write_all(&0x00000000_00000000u64.to_le_bytes()).unwrap(); // res2
223
f.write_all(&0x00000000_00000000u64.to_le_bytes()).unwrap(); // res3
224
f.write_all(&0x00000000_00000000u64.to_le_bytes()).unwrap(); // res4
225
f.write_all(&0x644D5241u32.to_le_bytes()).unwrap(); // magic
226
f.write_all(&0x00000000u32.to_le_bytes()).unwrap(); // res5
227
228
f.set_len(0xDC3808).unwrap();
229
f
230
}
231
232
fn mutate_file(mut f: &File, offset: u64, val: &[u8]) {
233
f.seek(SeekFrom::Start(offset))
234
.expect("failed to seek file");
235
f.write_all(val)
236
.expect("failed to write mutated value to file");
237
}
238
239
#[test]
240
fn load_arm64_valid() {
241
let gm = create_guest_mem();
242
let kernel_addr = GuestAddress(2 * 1024 * 1024);
243
let mut f = write_valid_kernel();
244
let kernel = load_arm64_kernel(&gm, kernel_addr, &mut f).unwrap();
245
assert_eq!(kernel.address_range.start, 0x107_0000);
246
assert_eq!(kernel.address_range.end, 0x1E3_3807);
247
assert_eq!(kernel.size, 0xDC_3808);
248
assert_eq!(kernel.entry, GuestAddress(0x107_0000));
249
}
250
251
#[test]
252
fn load_arm64_image_size_zero() {
253
let gm = create_guest_mem();
254
let kernel_addr = GuestAddress(2 * 1024 * 1024);
255
let mut f = write_valid_kernel();
256
257
// Set image_size = 0 and validate the default text_offset is applied.
258
mutate_file(&f, 16, &0u64.to_le_bytes());
259
260
let kernel = load_arm64_kernel(&gm, kernel_addr, &mut f).unwrap();
261
assert_eq!(kernel.address_range.start, 0x28_0000);
262
assert_eq!(kernel.address_range.end, 0x104_3807);
263
assert_eq!(kernel.size, 0xDC_3808);
264
assert_eq!(kernel.entry, GuestAddress(0x28_0000));
265
}
266
267
#[test]
268
fn load_arm64_bad_magic() {
269
let gm = create_guest_mem();
270
let kernel_addr = GuestAddress(2 * 1024 * 1024);
271
let mut f = write_valid_kernel();
272
273
// Mutate magic number so it doesn't match
274
mutate_file(&f, 56, &[0xCC, 0xCC, 0xCC, 0xCC]);
275
276
assert_eq!(
277
load_arm64_kernel(&gm, kernel_addr, &mut f),
278
Err(Error::InvalidMagicNumber)
279
);
280
}
281
282
fn write_valid_kernel_lz4() -> File {
283
let mut f = tempfile().expect("failed to create tempfile");
284
285
f.write_all(&0x184d2204u32.to_le_bytes()).unwrap(); // magic
286
f.write_all(&[0x44, 0x70, 0x1d]).unwrap(); // flg, bd, hc
287
288
// Compressed block #1.
289
f.write_all(&0x00004065u32.to_le_bytes()).unwrap();
290
f.write_all(&[
291
0x51, 0x00, 0xc0, 0x2e, 0x14, 0x00, 0x01, 0x00, 0x11, 0xe7, 0x06, 0x00, 0x11, 0x0a,
292
0x06, 0x00, 0x0f, 0x02, 0x00, 0x0f, 0x4f, 0x41, 0x52, 0x4d, 0x64, 0x26, 0x00, 0x0f,
293
0x0f, 0x02, 0x00,
294
])
295
.unwrap();
296
f.write_all(&[0xff; 16447]).unwrap();
297
298
// Compressed block #2.
299
f.write_all(&0x000050c9u32.to_le_bytes()).unwrap();
300
f.write_all(&[
301
0x00, 0x00, 0x00, 0x4b, 0x40, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x00,
302
])
303
.unwrap();
304
f.write_all(&[0xff; 16448]).unwrap();
305
306
// Compressed block #3.
307
f.write_all(&0x00005027u32.to_le_bytes()).unwrap();
308
f.write_all(&[
309
0x00, 0x00, 0x00, 0x4b, 0x40, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x00,
310
])
311
.unwrap();
312
f.write_all(&[0xff; 16448]).unwrap();
313
314
// Compressed block #4.
315
f.write_all(&0x00005027u32.to_le_bytes()).unwrap();
316
f.write_all(&[
317
0x00, 0x00, 0x00, 0x5f, 0x1c, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x00,
318
])
319
.unwrap();
320
f.write_all(&[0xff; 7252]).unwrap();
321
f.write_all(&[0x43, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00])
322
.unwrap();
323
324
// EndMark
325
f.write_all(&0x00000000u32.to_le_bytes()).unwrap();
326
327
// Checksum
328
f.write_all(&0x22a9944cu32.to_le_bytes()).unwrap();
329
330
f
331
}
332
333
fn write_valid_kernel_lz4_legacy() -> File {
334
let mut f = tempfile().expect("failed to create tempfile");
335
336
f.write_all(&0x184c2102u32.to_le_bytes()).unwrap(); // magic
337
338
// Compressed block #1.
339
f.write_all(&0x000080a6u32.to_le_bytes()).unwrap();
340
f.write_all(&[
341
0x51, 0x00, 0xc0, 0x2e, 0x14, 0x00, 0x01, 0x00, 0x11, 0xe7, 0x06, 0x00, 0x11, 0x0a,
342
0x06, 0x00, 0x0f, 0x02, 0x00, 0x0f, 0x4f, 0x41, 0x52, 0x4d, 0x64, 0x26, 0x00, 0x0f,
343
0x0f, 0x02, 0x00,
344
])
345
.unwrap();
346
f.write_all(&[0xff; 32896]).unwrap();
347
348
// Compressed block #2.
349
f.write_all(&0x0000500au32.to_le_bytes()).unwrap();
350
f.write_all(&[
351
0x00, 0x00, 0x00, 0x9f, 0x5c, 0x00, 0x00, 0x1f, 0x00, 0x01, 0x00,
352
])
353
.unwrap();
354
f.write_all(&[0xff; 23700]).unwrap();
355
f.write_all(&[0x83, 0x50, 0x00]).unwrap();
356
357
// EndMark
358
f.write_all(&[0x00, 0x00, 0x00, 0x00]).unwrap();
359
360
f
361
}
362
363
#[test]
364
fn load_arm64_lz4_valid() {
365
let gm = create_guest_mem();
366
let kernel_addr = GuestAddress(2 * 1024 * 1024);
367
let mut f = write_valid_kernel_lz4();
368
let kernel = load_arm64_kernel_lz4(&gm, kernel_addr, &mut f).unwrap();
369
assert_eq!(kernel.address_range.start, 0x107_0000);
370
assert_eq!(kernel.address_range.end, 0x1E3_3807);
371
assert_eq!(kernel.size, 0xDC_3808);
372
assert_eq!(kernel.entry, GuestAddress(0x107_0000));
373
}
374
375
#[test]
376
fn load_arm64_lz4_bad_magic() {
377
let gm = create_guest_mem();
378
let kernel_addr = GuestAddress(2 * 1024 * 1024);
379
let mut f = write_valid_kernel_lz4();
380
381
mutate_file(&f, 0, &[0xCC, 0xCC, 0xCC, 0xCC]);
382
383
assert_eq!(
384
load_arm64_kernel_lz4(&gm, kernel_addr, &mut f),
385
Err(Error::ReadHeader)
386
);
387
}
388
389
#[test]
390
fn load_arm64_lz4_bad_block() {
391
let gm = create_guest_mem();
392
let kernel_addr = GuestAddress(2 * 1024 * 1024);
393
let mut f = write_valid_kernel_lz4();
394
395
mutate_file(&f, 7, &[0xCC, 0xCC, 0xCC, 0xCC]);
396
397
assert_eq!(
398
load_arm64_kernel_lz4(&gm, kernel_addr, &mut f),
399
Err(Error::ReadHeader)
400
);
401
}
402
403
#[test]
404
fn load_arm64_lz4_legacy_valid() {
405
let gm = create_guest_mem();
406
let kernel_addr = GuestAddress(2 * 1024 * 1024);
407
let mut f = write_valid_kernel_lz4_legacy();
408
let kernel = load_arm64_kernel_lz4(&gm, kernel_addr, &mut f).unwrap();
409
assert_eq!(kernel.address_range.start, 0x107_0000);
410
assert_eq!(kernel.address_range.end, 0x1E3_3807);
411
assert_eq!(kernel.size, 0xDC_3808);
412
assert_eq!(kernel.entry, GuestAddress(0x107_0000));
413
}
414
415
#[test]
416
fn load_arm64_lz4_legacy_bad_magic() {
417
let gm = create_guest_mem();
418
let kernel_addr = GuestAddress(2 * 1024 * 1024);
419
let mut f = write_valid_kernel_lz4_legacy();
420
421
mutate_file(&f, 0, &[0xCC, 0xCC, 0xCC, 0xCC]);
422
423
assert_eq!(
424
load_arm64_kernel_lz4(&gm, kernel_addr, &mut f),
425
Err(Error::ReadHeader)
426
);
427
}
428
429
#[test]
430
fn load_arm64_lz4_legacy_bad_block() {
431
let gm = create_guest_mem();
432
let kernel_addr = GuestAddress(2 * 1024 * 1024);
433
let mut f = write_valid_kernel_lz4_legacy();
434
435
mutate_file(&f, 4, &[0xCC, 0xCC, 0xCC, 0xCC]);
436
437
assert_eq!(
438
load_arm64_kernel_lz4(&gm, kernel_addr, &mut f),
439
Err(Error::ReadHeader)
440
);
441
}
442
}
443
444