1 // Copyright 2018 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 use byteorder::{BigEndian, ByteOrder};
6 use libc::{c_char, c_int, c_void};
7 use std::ffi::{CStr, CString};
8 use std::fmt::{self, Display};
9 use std::io;
10 use std::ptr::null;
11
12 // This links to libfdt which handles the creation of the binary blob
13 // flattened device tree (fdt) that is passed to the kernel and indicates
14 // the hardware configuration of the machine.
15 #[link(name = "fdt")]
16 extern "C" {
fdt_create(buf: *mut c_void, bufsize: c_int) -> c_int17 fn fdt_create(buf: *mut c_void, bufsize: c_int) -> c_int;
fdt_finish_reservemap(fdt: *mut c_void) -> c_int18 fn fdt_finish_reservemap(fdt: *mut c_void) -> c_int;
fdt_begin_node(fdt: *mut c_void, name: *const c_char) -> c_int19 fn fdt_begin_node(fdt: *mut c_void, name: *const c_char) -> c_int;
fdt_property(fdt: *mut c_void, name: *const c_char, val: *const c_void, len: c_int) -> c_int20 fn fdt_property(fdt: *mut c_void, name: *const c_char, val: *const c_void, len: c_int)
21 -> c_int;
fdt_end_node(fdt: *mut c_void) -> c_int22 fn fdt_end_node(fdt: *mut c_void) -> c_int;
fdt_open_into(fdt: *const c_void, buf: *mut c_void, bufsize: c_int) -> c_int23 fn fdt_open_into(fdt: *const c_void, buf: *mut c_void, bufsize: c_int) -> c_int;
fdt_finish(fdt: *const c_void) -> c_int24 fn fdt_finish(fdt: *const c_void) -> c_int;
fdt_pack(fdt: *mut c_void) -> c_int25 fn fdt_pack(fdt: *mut c_void) -> c_int;
26 }
27
28 #[derive(Debug)]
29 pub enum Error {
30 FdtCreateError(c_int),
31 FdtFinishReservemapError(c_int),
32 FdtBeginNodeError(c_int),
33 FdtPropertyError(c_int),
34 FdtEndNodeError(c_int),
35 FdtOpenIntoError(c_int),
36 FdtFinishError(c_int),
37 FdtPackError(c_int),
38 FdtGuestMemoryWriteError,
39 FdtFileParseError,
40 FdtIoError(io::Error),
41 }
42
43 impl Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result44 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45 use self::Error::*;
46
47 write!(f, "libfdt: ")?;
48
49 match self {
50 FdtCreateError(ret) => write!(f, "error creating FDT, code={}", ret),
51 FdtFinishReservemapError(ret) => write!(f, "error finishing reserve map, code={}", ret),
52 FdtBeginNodeError(ret) => write!(f, "error beginning FDT node, code={}", ret),
53 FdtPropertyError(ret) => write!(f, "error adding FDT property, code={}", ret),
54 FdtEndNodeError(ret) => write!(f, "error ending FDT node, code={}", ret),
55 FdtOpenIntoError(ret) => write!(f, "error copying FDT to Guest, code={}", ret),
56 FdtFinishError(ret) => write!(f, "error performing FDT finish, code={}", ret),
57 FdtPackError(ret) => write!(f, "error packing FDT, code={}", ret),
58 FdtGuestMemoryWriteError => write!(f, "error writing FDT to guest memory"),
59 FdtFileParseError => write!(f, "parse error reading FDT parameters"),
60 FdtIoError(ret) => write!(f, "I/O error reading FDT parameters code={}", ret),
61 }
62 }
63 }
64
65 pub type Result<T> = std::result::Result<T, Error>;
66
67 impl std::error::Error for Error {}
68
begin_node(fdt: &mut Vec<u8>, name: &str) -> Result<()>69 pub fn begin_node(fdt: &mut Vec<u8>, name: &str) -> Result<()> {
70 let cstr_name = CString::new(name).unwrap();
71
72 // Safe because we allocated fdt and converted name to a CString
73 let fdt_ret = unsafe { fdt_begin_node(fdt.as_mut_ptr() as *mut c_void, cstr_name.as_ptr()) };
74 if fdt_ret != 0 {
75 return Err(Error::FdtBeginNodeError(fdt_ret));
76 }
77 Ok(())
78 }
79
end_node(fdt: &mut Vec<u8>) -> Result<()>80 pub fn end_node(fdt: &mut Vec<u8>) -> Result<()> {
81 // Safe because we allocated fdt
82 let fdt_ret = unsafe { fdt_end_node(fdt.as_mut_ptr() as *mut c_void) };
83 if fdt_ret != 0 {
84 return Err(Error::FdtEndNodeError(fdt_ret));
85 }
86 Ok(())
87 }
88
property(fdt: &mut Vec<u8>, name: &str, val: &[u8]) -> Result<()>89 pub fn property(fdt: &mut Vec<u8>, name: &str, val: &[u8]) -> Result<()> {
90 let cstr_name = CString::new(name).unwrap();
91 let val_ptr = val.as_ptr() as *const c_void;
92
93 // Safe because we allocated fdt and converted name to a CString
94 let fdt_ret = unsafe {
95 fdt_property(
96 fdt.as_mut_ptr() as *mut c_void,
97 cstr_name.as_ptr(),
98 val_ptr,
99 val.len() as i32,
100 )
101 };
102 if fdt_ret != 0 {
103 return Err(Error::FdtPropertyError(fdt_ret));
104 }
105 Ok(())
106 }
107
cpu_to_fdt32(input: u32) -> [u8; 4]108 fn cpu_to_fdt32(input: u32) -> [u8; 4] {
109 let mut buf = [0; 4];
110 BigEndian::write_u32(&mut buf, input);
111 buf
112 }
113
cpu_to_fdt64(input: u64) -> [u8; 8]114 fn cpu_to_fdt64(input: u64) -> [u8; 8] {
115 let mut buf = [0; 8];
116 BigEndian::write_u64(&mut buf, input);
117 buf
118 }
119
property_u32(fdt: &mut Vec<u8>, name: &str, val: u32) -> Result<()>120 pub fn property_u32(fdt: &mut Vec<u8>, name: &str, val: u32) -> Result<()> {
121 property(fdt, name, &cpu_to_fdt32(val))
122 }
123
property_u64(fdt: &mut Vec<u8>, name: &str, val: u64) -> Result<()>124 pub fn property_u64(fdt: &mut Vec<u8>, name: &str, val: u64) -> Result<()> {
125 property(fdt, name, &cpu_to_fdt64(val))
126 }
127
128 // Helper to generate a properly formatted byte vector using 32-bit cells
generate_prop32(cells: &[u32]) -> Vec<u8>129 pub fn generate_prop32(cells: &[u32]) -> Vec<u8> {
130 let mut ret: Vec<u8> = Vec::new();
131 for &e in cells {
132 ret.extend(&cpu_to_fdt32(e));
133 }
134 ret
135 }
136
137 // Helper to generate a properly formatted byte vector using 64-bit cells
generate_prop64(cells: &[u64]) -> Vec<u8>138 pub fn generate_prop64(cells: &[u64]) -> Vec<u8> {
139 let mut ret: Vec<u8> = Vec::new();
140 for &e in cells {
141 ret.extend(&cpu_to_fdt64(e));
142 }
143 ret
144 }
145
property_null(fdt: &mut Vec<u8>, name: &str) -> Result<()>146 pub fn property_null(fdt: &mut Vec<u8>, name: &str) -> Result<()> {
147 let cstr_name = CString::new(name).unwrap();
148
149 // Safe because we allocated fdt, converted name to a CString
150 let fdt_ret = unsafe {
151 fdt_property(
152 fdt.as_mut_ptr() as *mut c_void,
153 cstr_name.as_ptr(),
154 null(),
155 0,
156 )
157 };
158 if fdt_ret != 0 {
159 return Err(Error::FdtPropertyError(fdt_ret));
160 }
161 Ok(())
162 }
163
property_cstring(fdt: &mut Vec<u8>, name: &str, cstr_value: &CStr) -> Result<()>164 pub fn property_cstring(fdt: &mut Vec<u8>, name: &str, cstr_value: &CStr) -> Result<()> {
165 let value_bytes = cstr_value.to_bytes_with_nul();
166 let cstr_name = CString::new(name).unwrap();
167
168 // Safe because we allocated fdt, converted name and value to CStrings
169 let fdt_ret = unsafe {
170 fdt_property(
171 fdt.as_mut_ptr() as *mut c_void,
172 cstr_name.as_ptr(),
173 value_bytes.as_ptr() as *mut c_void,
174 value_bytes.len() as i32,
175 )
176 };
177 if fdt_ret != 0 {
178 return Err(Error::FdtPropertyError(fdt_ret));
179 }
180 Ok(())
181 }
182
property_string(fdt: &mut Vec<u8>, name: &str, value: &str) -> Result<()>183 pub fn property_string(fdt: &mut Vec<u8>, name: &str, value: &str) -> Result<()> {
184 let cstr_value = CString::new(value).unwrap();
185 property_cstring(fdt, name, &cstr_value)
186 }
187
start_fdt(fdt: &mut Vec<u8>, fdt_max_size: usize) -> Result<()>188 pub fn start_fdt(fdt: &mut Vec<u8>, fdt_max_size: usize) -> Result<()> {
189 // Safe since we allocated this array with fdt_max_size
190 let mut fdt_ret = unsafe { fdt_create(fdt.as_mut_ptr() as *mut c_void, fdt_max_size as c_int) };
191
192 if fdt_ret != 0 {
193 return Err(Error::FdtCreateError(fdt_ret));
194 }
195 // Safe since we allocated this array
196 fdt_ret = unsafe { fdt_finish_reservemap(fdt.as_mut_ptr() as *mut c_void) };
197 if fdt_ret != 0 {
198 return Err(Error::FdtFinishReservemapError(fdt_ret));
199 }
200 Ok(())
201 }
202
finish_fdt(fdt: &mut Vec<u8>, fdt_final: &mut Vec<u8>, fdt_max_size: usize) -> Result<()>203 pub fn finish_fdt(fdt: &mut Vec<u8>, fdt_final: &mut Vec<u8>, fdt_max_size: usize) -> Result<()> {
204 // Safe since we allocated fdt_final and previously passed in it's size
205 let mut fdt_ret = unsafe { fdt_finish(fdt.as_mut_ptr() as *mut c_void) };
206 if fdt_ret != 0 {
207 return Err(Error::FdtFinishError(fdt_ret));
208 }
209
210 // Safe because we allocated both arrays with the correct size
211 fdt_ret = unsafe {
212 fdt_open_into(
213 fdt.as_mut_ptr() as *mut c_void,
214 fdt_final.as_mut_ptr() as *mut c_void,
215 fdt_max_size as i32,
216 )
217 };
218 if fdt_ret != 0 {
219 return Err(Error::FdtOpenIntoError(fdt_ret));
220 }
221
222 // Safe since we allocated fdt_final
223 fdt_ret = unsafe { fdt_pack(fdt_final.as_mut_ptr() as *mut c_void) };
224 if fdt_ret != 0 {
225 return Err(Error::FdtPackError(fdt_ret));
226 }
227 Ok(())
228 }
229