command_line.rs 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. //! Module for [CommandLineTag].
  2. use crate::{Tag, TagTrait, TagTypeId};
  3. use core::fmt::{Debug, Formatter};
  4. use core::mem;
  5. use core::str;
  6. #[cfg(feature = "builder")]
  7. use {
  8. crate::builder::traits::StructAsBytes, crate::builder::BoxedDst, crate::TagType,
  9. alloc::vec::Vec, core::convert::TryInto,
  10. };
  11. pub(crate) const METADATA_SIZE: usize = mem::size_of::<TagTypeId>() + mem::size_of::<u32>();
  12. /// This tag contains the command line string.
  13. ///
  14. /// The string is a normal C-style UTF-8 zero-terminated string that can be
  15. /// obtained via the `command_line` method.
  16. #[derive(ptr_meta::Pointee, PartialEq, Eq, PartialOrd, Ord, Hash)]
  17. #[repr(C)]
  18. pub struct CommandLineTag {
  19. typ: TagTypeId,
  20. size: u32,
  21. /// Null-terminated UTF-8 string
  22. cmdline: [u8],
  23. }
  24. impl CommandLineTag {
  25. /// Create a new command line tag from the given string.
  26. #[cfg(feature = "builder")]
  27. pub fn new(command_line: &str) -> BoxedDst<Self> {
  28. let mut bytes: Vec<_> = command_line.bytes().collect();
  29. if !bytes.ends_with(&[0]) {
  30. // terminating null-byte
  31. bytes.push(0);
  32. }
  33. BoxedDst::new(TagType::Cmdline, &bytes)
  34. }
  35. /// Reads the command line of the kernel as Rust string slice without
  36. /// the null-byte.
  37. ///
  38. /// For example, this returns `"console=ttyS0"`.if the GRUB config
  39. /// contains `"multiboot2 /mykernel console=ttyS0"`.
  40. ///
  41. /// If the function returns `Err` then perhaps the memory is invalid.
  42. ///
  43. /// # Examples
  44. ///
  45. /// ```rust,no_run
  46. /// # let boot_info = unsafe { multiboot2::load(0xdeadbeef).unwrap() };
  47. /// if let Some(tag) = boot_info.command_line_tag() {
  48. /// let command_line = tag.cmdline();
  49. /// assert_eq!(Ok("/bootarg"), command_line);
  50. /// }
  51. /// ```
  52. pub fn cmdline(&self) -> Result<&str, str::Utf8Error> {
  53. Tag::get_dst_str_slice(&self.cmdline)
  54. }
  55. }
  56. impl Debug for CommandLineTag {
  57. fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
  58. f.debug_struct("CommandLineTag")
  59. .field("typ", &{ self.typ })
  60. .field("size", &{ self.size })
  61. .field("cmdline", &self.cmdline())
  62. .finish()
  63. }
  64. }
  65. impl TagTrait for CommandLineTag {
  66. fn dst_size(base_tag: &Tag) -> usize {
  67. assert!(base_tag.size as usize >= METADATA_SIZE);
  68. base_tag.size as usize - METADATA_SIZE
  69. }
  70. }
  71. #[cfg(feature = "builder")]
  72. impl StructAsBytes for CommandLineTag {
  73. fn byte_size(&self) -> usize {
  74. self.size.try_into().unwrap()
  75. }
  76. }
  77. #[cfg(test)]
  78. mod tests {
  79. use crate::{CommandLineTag, Tag, TagType};
  80. const MSG: &str = "hello";
  81. /// Returns the tag structure in bytes in native endian format.
  82. fn get_bytes() -> std::vec::Vec<u8> {
  83. // size is: 4 bytes for tag + 4 bytes for size + length of null-terminated string
  84. let size = (4 + 4 + MSG.as_bytes().len() + 1) as u32;
  85. [
  86. &((TagType::Cmdline.val()).to_le_bytes()),
  87. &size.to_le_bytes(),
  88. MSG.as_bytes(),
  89. // Null Byte
  90. &[0],
  91. ]
  92. .iter()
  93. .flat_map(|bytes| bytes.iter())
  94. .copied()
  95. .collect()
  96. }
  97. /// Tests to parse a string with a terminating null byte from the tag (as the spec defines).
  98. #[test]
  99. fn test_parse_str() {
  100. let tag = get_bytes();
  101. let tag = unsafe { &*tag.as_ptr().cast::<Tag>() };
  102. let tag = tag.cast_tag::<CommandLineTag>();
  103. assert_eq!({ tag.typ }, TagType::Cmdline);
  104. assert_eq!(tag.cmdline().expect("must be valid UTF-8"), MSG);
  105. }
  106. /// Test to generate a tag from a given string.
  107. #[test]
  108. #[cfg(feature = "builder")]
  109. fn test_build_str() {
  110. use crate::builder::traits::StructAsBytes;
  111. let tag = CommandLineTag::new(MSG);
  112. let bytes = tag.struct_as_bytes();
  113. assert_eq!(bytes, get_bytes());
  114. assert_eq!(tag.cmdline(), Ok(MSG));
  115. // test also some bigger message
  116. let tag = CommandLineTag::new("AbCdEfGhUjK YEAH");
  117. assert_eq!(tag.cmdline(), Ok("AbCdEfGhUjK YEAH"));
  118. let tag = CommandLineTag::new("AbCdEfGhUjK YEAH".repeat(42).as_str());
  119. assert_eq!(tag.cmdline(), Ok("AbCdEfGhUjK YEAH".repeat(42).as_str()));
  120. }
  121. }