1// This module defines the `ComponentName1` type and marks it deprecated. That
2// causes warnings for uses within this module (e.g. the `impl ComponentName1`
3// block), so turn off deprecated warnings. It's not yet possible to make this
4// allow more fine-grained, see https://github.com/rust-lang/rust/issues/62398.
5#![allow(deprecated)]
6
7use crate::proto::unsafe_protocol;
8use crate::table::boot::{BootServices, ScopedProtocol};
9use crate::{CStr16, Error, Handle, Result, Status};
10use core::{ptr, slice};
11
12/// Protocol that provides human-readable names for a driver and for each of the
13/// controllers that the driver is managing.
14///
15/// This protocol was deprecated in UEFI 2.1 in favor of the new
16/// [`ComponentName2`] protocol. The two protocols are identical except the
17/// encoding of supported languages changed from [ISO 639-2] to [RFC 4646]. The
18/// [`ComponentName`] wrapper can be used to automatically select
19/// [`ComponentName2`] if available, and otherwise fall back to
20/// [`ComponentName1`].
21///
22/// The corresponding C type is `EFI_COMPONENT_NAME_PROTOCOL`.
23///
24/// [ISO 639-2]: https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes
25/// [RFC 4646]: https://www.rfc-editor.org/rfc/rfc4646
26#[deprecated = "deprecated in UEFI 2.1; use ComponentName2 where possible"]
27#[unsafe_protocol("107a772c-d5e1-11d4-9a46-0090273fc14d")]
28#[repr(C)]
29pub struct ComponentName1 {
30 get_driver_name: unsafe extern "efiapi" fn(
31 this: *const Self,
32 language: *const u8,
33 driver_name: *mut *const u16,
34 ) -> Status,
35 get_controller_name: unsafe extern "efiapi" fn(
36 this: *const Self,
37 controller_handle: Handle,
38 child_handle: Option<Handle>,
39 language: *const u8,
40 controller_name: *mut *const u16,
41 ) -> Status,
42 supported_languages: *const u8,
43}
44
45impl ComponentName1 {
46 /// Get an iterator over supported languages. Each language is identified by
47 /// a three-letter ASCII string specified in [ISO 639-2]. For example,
48 /// English is encoded as "eng".
49 ///
50 /// [ISO 639-2]: https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes
51 pub fn supported_languages(&self) -> core::result::Result<LanguageIter, LanguageError> {
52 LanguageIter::new(self.supported_languages, LanguageIterKind::V1)
53 }
54
55 /// Get the human-readable name of the driver in the given language.
56 ///
57 /// `language` must be one of the languages returned by [`supported_languages`].
58 ///
59 /// [`supported_languages`]: Self::supported_languages
60 pub fn driver_name(&self, language: &str) -> Result<&CStr16> {
61 let language = language_to_cstr(language)?;
62 let mut driver_name = ptr::null();
63 unsafe { (self.get_driver_name)(self, language.as_ptr(), &mut driver_name) }
64 .into_with_val(|| unsafe { CStr16::from_ptr(driver_name.cast()) })
65 }
66
67 /// Get the human-readable name of a controller in the given language.
68 ///
69 /// `language` must be one of the languages returned by [`supported_languages`].
70 ///
71 /// [`supported_languages`]: Self::supported_languages
72 pub fn controller_name(
73 &self,
74 controller_handle: Handle,
75 child_handle: Option<Handle>,
76 language: &str,
77 ) -> Result<&CStr16> {
78 let language = language_to_cstr(language)?;
79 let mut driver_name = ptr::null();
80 unsafe {
81 (self.get_controller_name)(
82 self,
83 controller_handle,
84 child_handle,
85 language.as_ptr(),
86 &mut driver_name,
87 )
88 }
89 .into_with_val(|| unsafe { CStr16::from_ptr(driver_name.cast()) })
90 }
91}
92
93/// Protocol that provides human-readable names for a driver and for each of the
94/// controllers that the driver is managing.
95///
96/// This protocol was introduced in UEFI 2.1 to replace the now-deprecated
97/// [`ComponentName1`] protocol. The two protocols are identical except the
98/// encoding of supported languages changed from [ISO 639-2] to [RFC 4646]. The
99/// [`ComponentName`] wrapper can be used to automatically select
100/// [`ComponentName2`] if available, and otherwise fall back to
101/// [`ComponentName1`].
102///
103/// The corresponding C type is `EFI_COMPONENT_NAME2_PROTOCOL`.
104///
105/// [ISO 639-2]: https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes
106/// [RFC 4646]: https://www.rfc-editor.org/rfc/rfc4646
107#[unsafe_protocol("6a7a5cff-e8d9-4f70-bada-75ab3025ce14")]
108#[repr(C)]
109pub struct ComponentName2 {
110 get_driver_name: unsafe extern "efiapi" fn(
111 this: *const Self,
112 language: *const u8,
113 driver_name: *mut *const u16,
114 ) -> Status,
115 get_controller_name: unsafe extern "efiapi" fn(
116 this: *const Self,
117 controller_handle: Handle,
118 child_handle: Option<Handle>,
119 language: *const u8,
120 controller_name: *mut *const u16,
121 ) -> Status,
122 supported_languages: *const u8,
123}
124
125impl ComponentName2 {
126 /// Get an iterator over supported languages. Each language is identified by
127 /// an ASCII string specified in [RFC 4646]. For example, English is encoded
128 /// as "en".
129 ///
130 /// [RFC 4646]: https://www.rfc-editor.org/rfc/rfc4646
131 pub fn supported_languages(&self) -> core::result::Result<LanguageIter, LanguageError> {
132 LanguageIter::new(self.supported_languages, LanguageIterKind::V2)
133 }
134
135 /// Get the human-readable name of the driver in the given language.
136 ///
137 /// `language` must be one of the languages returned by [`supported_languages`].
138 ///
139 /// [`supported_languages`]: Self::supported_languages
140 pub fn driver_name(&self, language: &str) -> Result<&CStr16> {
141 let language = language_to_cstr(language)?;
142 let mut driver_name = ptr::null();
143 unsafe { (self.get_driver_name)(self, language.as_ptr(), &mut driver_name) }
144 .into_with_val(|| unsafe { CStr16::from_ptr(driver_name.cast()) })
145 }
146
147 /// Get the human-readable name of a controller in the given language.
148 ///
149 /// `language` must be one of the languages returned by [`supported_languages`].
150 ///
151 /// [`supported_languages`]: Self::supported_languages
152 pub fn controller_name(
153 &self,
154 controller_handle: Handle,
155 child_handle: Option<Handle>,
156 language: &str,
157 ) -> Result<&CStr16> {
158 let language = language_to_cstr(language)?;
159 let mut driver_name = ptr::null();
160 unsafe {
161 (self.get_controller_name)(
162 self,
163 controller_handle,
164 child_handle,
165 language.as_ptr(),
166 &mut driver_name,
167 )
168 }
169 .into_with_val(|| unsafe { CStr16::from_ptr(driver_name.cast()) })
170 }
171}
172
173/// Wrapper around [`ComponentName1`] and [`ComponentName2`]. This will use
174/// [`ComponentName2`] if available, otherwise it will back to
175/// [`ComponentName1`].
176pub enum ComponentName<'a> {
177 /// Opened [`ComponentName1`] protocol.
178 V1(ScopedProtocol<'a, ComponentName1>),
179
180 /// Opened [`ComponentName2`] protocol.
181 V2(ScopedProtocol<'a, ComponentName2>),
182}
183
184impl<'a> ComponentName<'a> {
185 /// Open the [`ComponentName2`] protocol if available, otherwise fall back to
186 /// [`ComponentName1`].
187 pub fn open(boot_services: &'a BootServices, handle: Handle) -> Result<Self> {
188 if let Ok(cn2) = boot_services.open_protocol_exclusive::<ComponentName2>(handle) {
189 Ok(Self::V2(cn2))
190 } else {
191 Ok(Self::V1(
192 boot_services.open_protocol_exclusive::<ComponentName1>(handle)?,
193 ))
194 }
195 }
196
197 /// Get an iterator over supported languages. Each language is identified by
198 /// an ASCII string. If the opened protocol is [`ComponentName1`] this will
199 /// be an [ISO 639-2] string. If the opened protocol is [`ComponentName2`]
200 /// it will be an [RFC 4646] string. For example, English is encoded as
201 /// "eng" in ISO 639-2, and "en" in RFC 4646.
202 ///
203 /// [ISO 639-2]: https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes
204 /// [RFC 4646]: https://www.rfc-editor.org/rfc/rfc4646
205 pub fn supported_languages(&self) -> core::result::Result<LanguageIter, LanguageError> {
206 match self {
207 Self::V1(cn1) => cn1.supported_languages(),
208 Self::V2(cn2) => cn2.supported_languages(),
209 }
210 }
211
212 /// Get the human-readable name of the driver in the given language.
213 ///
214 /// `language` must be one of the languages returned by [`supported_languages`].
215 ///
216 /// [`supported_languages`]: Self::supported_languages
217 pub fn driver_name(&self, language: &str) -> Result<&CStr16> {
218 match self {
219 Self::V1(cn1) => cn1.driver_name(language),
220 Self::V2(cn2) => cn2.driver_name(language),
221 }
222 }
223
224 /// Get the human-readable name of a controller in the given language.
225 ///
226 /// `language` must be one of the languages returned by [`supported_languages`].
227 ///
228 /// [`supported_languages`]: Self::supported_languages
229 pub fn controller_name(
230 &self,
231 controller_handle: Handle,
232 child_handle: Option<Handle>,
233 language: &str,
234 ) -> Result<&CStr16> {
235 match self {
236 Self::V1(cn1) => cn1.controller_name(controller_handle, child_handle, language),
237 Self::V2(cn2) => cn2.controller_name(controller_handle, child_handle, language),
238 }
239 }
240}
241
242/// Error returned by [`ComponentName1::supported_languages`] and
243/// [`ComponentName2::supported_languages`].
244#[derive(Debug, Eq, PartialEq)]
245pub enum LanguageError {
246 /// The supported languages list contains a non-ASCII character at the
247 /// specified index.
248 Ascii {
249 /// Index of the invalid character.
250 index: usize,
251 },
252}
253
254#[derive(PartialEq)]
255enum LanguageIterKind {
256 V1,
257 V2,
258}
259
260/// Iterator returned by [`ComponentName1::supported_languages`] and
261/// [`ComponentName2::supported_languages`].
262pub struct LanguageIter<'a> {
263 languages: &'a [u8],
264 kind: LanguageIterKind,
265}
266
267impl<'a> LanguageIter<'a> {
268 fn new(
269 languages: *const u8,
270 kind: LanguageIterKind,
271 ) -> core::result::Result<Self, LanguageError> {
272 let mut index: usize = 0;
273 loop {
274 let c: u8 = unsafe { languages.add(count:index).read() };
275 if c == 0 {
276 break;
277 } else if !c.is_ascii() {
278 return Err(LanguageError::Ascii { index });
279 } else {
280 index += 1;
281 }
282 }
283
284 Ok(Self {
285 languages: unsafe { slice::from_raw_parts(data:languages, len:index) },
286 kind,
287 })
288 }
289}
290
291impl<'a> Iterator for LanguageIter<'a> {
292 type Item = &'a str;
293
294 fn next(&mut self) -> Option<Self::Item> {
295 if self.languages.is_empty() {
296 return None;
297 }
298
299 let lang;
300 match self.kind {
301 LanguageIterKind::V1 => {
302 if self.languages.len() <= 3 {
303 lang = self.languages;
304 self.languages = &[];
305 } else {
306 lang = &self.languages[..3];
307 self.languages = &self.languages[3..];
308 }
309 }
310 LanguageIterKind::V2 => {
311 if let Some(index) = self.languages.iter().position(|c| *c == b';') {
312 lang = &self.languages[..index];
313 self.languages = &self.languages[index + 1..];
314 } else {
315 lang = self.languages;
316 self.languages = &[];
317 }
318 }
319 }
320
321 // OK to unwrap because we already checked the string is ASCII.
322 Some(core::str::from_utf8(lang).unwrap())
323 }
324}
325
326/// Statically-sized buffer used to convert a `str` to a null-terminated C
327/// string. The buffer should be at least 42 characters per
328/// <https://www.rfc-editor.org/rfc/rfc4646#section-4.3.1>, plus one for the
329/// null terminator. Round up to 64 bytes just for aesthetics.
330type LanguageCStr = [u8; 64];
331
332fn language_to_cstr(language: &str) -> Result<LanguageCStr> {
333 let mut lang_cstr: LanguageCStr = [0; 64];
334 // Ensure there's room for a null-terminator.
335 if language.len() >= lang_cstr.len() - 1 {
336 return Err(Error::from(Status::BUFFER_TOO_SMALL));
337 }
338 lang_cstr[..language.len()].copy_from_slice(src:language.as_bytes());
339 // Assert that it's null-terminated.
340 assert_eq!(*lang_cstr.last().unwrap(), 0);
341 Ok(lang_cstr)
342}
343
344#[cfg(test)]
345mod tests {
346 use super::*;
347 use alloc::vec::Vec;
348 use LanguageIterKind::{V1, V2};
349
350 #[test]
351 fn test_language_iter_v1() {
352 // Empty string.
353 let data = "\0";
354 assert!(LanguageIter::new(data.as_ptr(), V1)
355 .unwrap()
356 .next()
357 .is_none());
358
359 // Two languages.
360 let data = "engfra\0";
361 assert_eq!(
362 LanguageIter::new(data.as_ptr(), V1)
363 .unwrap()
364 .collect::<Vec<_>>(),
365 ["eng", "fra"]
366 );
367
368 // Truncated data.
369 let data = "en\0";
370 assert_eq!(
371 LanguageIter::new(data.as_ptr(), V1)
372 .unwrap()
373 .collect::<Vec<_>>(),
374 ["en"]
375 );
376
377 // Non-ASCII.
378 let data = "engæ\0";
379 assert_eq!(
380 LanguageIter::new(data.as_ptr(), V1).err().unwrap(),
381 LanguageError::Ascii { index: 3 },
382 );
383 }
384
385 #[test]
386 fn test_language_iter_v2() {
387 // Empty string.
388 let data = "\0";
389 assert!(LanguageIter::new(data.as_ptr(), V2)
390 .unwrap()
391 .next()
392 .is_none());
393
394 // Two languages.
395 let data = "en;fr\0";
396 assert_eq!(
397 LanguageIter::new(data.as_ptr(), V2)
398 .unwrap()
399 .collect::<Vec<_>>(),
400 ["en", "fr"]
401 );
402
403 // Non-ASCII.
404 let data = "engæ\0";
405 assert_eq!(
406 LanguageIter::new(data.as_ptr(), V2).err().unwrap(),
407 LanguageError::Ascii { index: 3 },
408 );
409 }
410
411 #[test]
412 fn test_language_to_cstr() {
413 let mut expected = [0; 64];
414 expected[0] = b'e';
415 expected[1] = b'n';
416 assert_eq!(language_to_cstr("en"), Ok(expected));
417
418 assert_eq!(
419 language_to_cstr(
420 "0123456789012345678901234567890123456789012345678901234567890123456789"
421 )
422 .err()
423 .unwrap()
424 .status(),
425 Status::BUFFER_TOO_SMALL
426 );
427 }
428}
429