[Bind] Add device properties fallback

The stored device properties might not contain the
protocol ID or autobind data. As such, when matching
a driver to a device, the protocol ID and autobind
values need to be added to the device properties.

Bug: 74316
Testing: Added unit tests in binding_v2_tests.cc
Change-Id: I2d55bc79ac8db507216b2158988db0e8809f6909
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/514108
Commit-Queue: Sarah Chan <spqchan@google.com>
Reviewed-by: Alex Legg <alexlegg@google.com>
Reviewed-by: David Gilhooley <dgilhooley@google.com>
diff --git a/src/devices/bin/driver_manager/binding.cc b/src/devices/bin/driver_manager/binding.cc
index 9a9d9c5..4a6057c 100644
--- a/src/devices/bin/driver_manager/binding.cc
+++ b/src/devices/bin/driver_manager/binding.cc
@@ -158,8 +158,8 @@
       properties[i] = device_property_t{.key = props[i].id, .value = props[i].value};
     }
 
-    return match_bind_rules(drv->binding_size, bytecode ? bytecode->get() : nullptr, props.size(),
-                            properties.get());
+    return match_bind_rules(bytecode ? bytecode->get() : nullptr, drv->binding_size,
+                            properties.get(), props.size(), protocol_id, autobind);
   }
 
   LOGF(ERROR, "Invalid bytecode version: %i", drv->bytecode_version);
diff --git a/src/devices/bin/driver_manager/binding_v2_test.cc b/src/devices/bin/driver_manager/binding_v2_test.cc
index 7411000c..e2d0766 100644
--- a/src/devices/bin/driver_manager/binding_v2_test.cc
+++ b/src/devices/bin/driver_manager/binding_v2_test.cc
@@ -12,7 +12,7 @@
 // Tests for matching device properties to the new bytecode.
 
 bool match_bind_rules(const uint8_t* bytecode, const zx_device_prop_t* props, size_t bytecode_sz,
-                      size_t props_sz) {
+                      size_t props_sz, uint32_t protocol_id, bool autobind) {
   auto driver = Driver();
   driver.bytecode_version = 2;
 
@@ -28,7 +28,7 @@
     memcpy(properties.data(), props, sizeof(props[0]) * props_sz);
   }
 
-  return driver_is_bindable(&driver, 0, std::move(properties), false);
+  return driver_is_bindable(&driver, protocol_id, std::move(properties), autobind);
 }
 
 TEST(BindingV2Test, SingleAbortInstruction) {
@@ -39,7 +39,8 @@
       0x30                                          // Abort instruction
   };
   zx_device_prop_t properties[] = {zx_device_prop_t{5, 0, 2}};
-  ASSERT_FALSE(match_bind_rules(bytecode, properties, std::size(bytecode), std::size(properties)));
+  ASSERT_FALSE(
+      match_bind_rules(bytecode, properties, std::size(bytecode), std::size(properties), 5, false));
 }
 
 TEST(BindingV2Test, NoBindRules) {
@@ -49,7 +50,8 @@
       0x49, 0x4E, 0x53, 0x54, 0x0,  0x0, 0x0, 0x0   // Instruction header
   };
   zx_device_prop_t properties[] = {zx_device_prop_t{5, 0, 2}};
-  ASSERT_TRUE(match_bind_rules(bytecode, properties, std::size(bytecode), std::size(properties)));
+  ASSERT_TRUE(
+      match_bind_rules(bytecode, properties, std::size(bytecode), std::size(properties), 5, false));
 }
 
 TEST(BindingV2Test, MatchDeviceProperty) {
@@ -60,7 +62,8 @@
       0x01, 0x01, 0x05, 0x0,  0x0,  0x0, 0x01, 0x02, 0x0, 0x0, 0x0,  // Equal instruction
   };
   zx_device_prop_t properties[] = {zx_device_prop_t{4, 0, 3}, zx_device_prop_t{5, 0, 2}};
-  ASSERT_TRUE(match_bind_rules(bytecode, properties, std::size(bytecode), std::size(properties)));
+  ASSERT_TRUE(
+      match_bind_rules(bytecode, properties, std::size(bytecode), std::size(properties), 5, false));
 }
 
 TEST(BindingV2Test, MismatchDeviceProperty) {
@@ -71,22 +74,56 @@
       0x01, 0x01, 0x05, 0x0,  0x0,  0x0, 0x01, 0x02, 0x0, 0x0, 0x0,  // Equal instruction
   };
   zx_device_prop_t properties[] = {zx_device_prop_t{5, 0, 20}};
-  ASSERT_FALSE(match_bind_rules(bytecode, properties, std::size(bytecode), std::size(properties)));
+  ASSERT_FALSE(
+      match_bind_rules(bytecode, properties, std::size(bytecode), std::size(properties), 5, false));
 }
 
-TEST(BindingV2Test, NoDeviceProperties) {
+TEST(BindingV2Test, NoDevicePropertiesWithMismatchProtocolId) {
   uint8_t bytecode[] = {
       0x42, 0x49, 0x4E, 0x44, 0x02, 0x0, 0x0,  0x0,                  // Bind header
       0x53, 0x59, 0x4E, 0x42, 0x0,  0x0, 0x0,  0x0,                  // Symbol table header
       0x49, 0x4E, 0x53, 0x54, 0x0B, 0x0, 0x0,  0x0,                  // Instruction header
-      0x01, 0x01, 0x05, 0x0,  0x0,  0x0, 0x01, 0x02, 0x0, 0x0, 0x0,  // Equal instruction
+      0x01, 0x01, 0x01, 0x0,  0x0,  0x0, 0x01, 0x02, 0x0, 0x0, 0x0,  // Equal instruction
   };
   zx_device_prop_t properties[] = {};
-  ASSERT_FALSE(match_bind_rules(bytecode, properties, std::size(bytecode), 0));
+  ASSERT_FALSE(match_bind_rules(bytecode, properties, std::size(bytecode), 0, 5, false));
+}
+
+TEST(BindingV2Test, NoDevicePropertiesWithMatchingProtocolId) {
+  uint8_t bytecode[] = {
+      0x42, 0x49, 0x4E, 0x44, 0x02, 0x0, 0x0,  0x0,                  // Bind header
+      0x53, 0x59, 0x4E, 0x42, 0x0,  0x0, 0x0,  0x0,                  // Symbol table header
+      0x49, 0x4E, 0x53, 0x54, 0x0B, 0x0, 0x0,  0x0,                  // Instruction header
+      0x01, 0x01, 0x01, 0x0,  0x0,  0x0, 0x01, 0x05, 0x0, 0x0, 0x0,  // Equal instruction
+  };
+  zx_device_prop_t properties[] = {};
+  ASSERT_TRUE(match_bind_rules(bytecode, properties, std::size(bytecode), 0, 5, false));
+}
+
+TEST(BindingV2Test, NoDevicePropertiesWithMismatchAutobind) {
+  uint8_t bytecode[] = {
+      0x42, 0x49, 0x4E, 0x44, 0x02, 0x0, 0x0,  0x0,                  // Bind header
+      0x53, 0x59, 0x4E, 0x42, 0x0,  0x0, 0x0,  0x0,                  // Symbol table header
+      0x49, 0x4E, 0x53, 0x54, 0x0B, 0x0, 0x0,  0x0,                  // Instruction header
+      0x01, 0x01, 0x02, 0x0,  0x0,  0x0, 0x01, 0x01, 0x0, 0x0, 0x0,  //  Equal instruction
+  };
+  zx_device_prop_t properties[] = {};
+  ASSERT_FALSE(match_bind_rules(bytecode, properties, std::size(bytecode), 0, 5, false));
+}
+
+TEST(BindingV2Test, NoDevicePropertiesWithMatchingAutobind) {
+  uint8_t bytecode[] = {
+      0x42, 0x49, 0x4E, 0x44, 0x02, 0x0, 0x0,  0x0,                  // Bind header
+      0x53, 0x59, 0x4E, 0x42, 0x0,  0x0, 0x0,  0x0,                  // Symbol table header
+      0x49, 0x4E, 0x53, 0x54, 0x0B, 0x0, 0x0,  0x0,                  // Instruction header
+      0x01, 0x01, 0x02, 0x0,  0x0,  0x0, 0x01, 0x01, 0x0, 0x0, 0x0,  //  Equal instruction
+  };
+  zx_device_prop_t properties[] = {};
+  ASSERT_TRUE(match_bind_rules(bytecode, properties, std::size(bytecode), 0, 5, true));
 }
 
 TEST(BindingV2Test, EmptyBytecode) {
   uint8_t bytecode[] = {};
   zx_device_prop_t properties[] = {zx_device_prop_t{5, 0, 20}};
-  ASSERT_FALSE(match_bind_rules(bytecode, properties, 0, std::size(properties)));
+  ASSERT_FALSE(match_bind_rules(bytecode, properties, 0, std::size(properties), 5, false));
 }
diff --git a/src/devices/lib/bind/ffi_bindings.h b/src/devices/lib/bind/ffi_bindings.h
index ba68996..42ff272 100644
--- a/src/devices/lib/bind/ffi_bindings.h
+++ b/src/devices/lib/bind/ffi_bindings.h
@@ -11,8 +11,9 @@
 };
 
 extern "C" {
-bool match_bind_rules(size_t bytecode_sz, const uint8_t *bytecode, size_t properties_sz,
-                      const device_property_t *properties);
+bool match_bind_rules(const uint8_t *bytecode, size_t bytecode_sz,
+                      const device_property_t *properties, size_t properties_sz,
+                      uint8_t protocol_id, bool autobind);
 }
 
 #endif  // SRC_DEVICES_LIB_BIND_FFI_BINDINGS_H_
diff --git a/src/devices/lib/bind/src/ffi.rs b/src/devices/lib/bind/src/ffi.rs
index d5520f7..8ee748e 100644
--- a/src/devices/lib/bind/src/ffi.rs
+++ b/src/devices/lib/bind/src/ffi.rs
@@ -6,28 +6,33 @@
 use bind::match_bind::{match_bytecode, PropertyKey};
 use std::collections::HashMap;
 
+const BIND_PROTOCOL: u64 = 0x0001;
+const BIND_AUTOBIND: u64 = 0x0002;
+
 #[repr(C)]
 pub struct device_property_t {
     key: u32,
     value: u32,
 }
 
+// |bytecode_sz| must match the size of |bytecode_c|. |properties_sz| must
+// match the size o |properties_c|.
 #[no_mangle]
-pub unsafe extern "C" fn match_bind_rules(
-    bytecode_sz: libc::size_t,
+pub extern "C" fn match_bind_rules(
     bytecode_c: *const u8,
-    properties_sz: libc::size_t,
+    bytecode_sz: libc::size_t,
     properties_c: *const device_property_t,
+    properties_sz: libc::size_t,
+    protocol_id: u32,
+    autobind: bool,
 ) -> bool {
     if bytecode_c.is_null() {
         return false;
     }
 
-    let bytecode = std::slice::from_raw_parts(bytecode_c, bytecode_sz).to_vec();
-
     let mut device_properties = HashMap::new();
     if !properties_c.is_null() {
-        let properties = std::slice::from_raw_parts(properties_c, properties_sz);
+        let properties = unsafe { std::slice::from_raw_parts(properties_c, properties_sz) };
         for property in properties.iter() {
             device_properties.insert(
                 PropertyKey::NumberKey(property.key as u64),
@@ -36,6 +41,16 @@
         }
     }
 
+    // Insert the protocol ID property if it's missing.
+    device_properties
+        .entry(PropertyKey::NumberKey(BIND_PROTOCOL))
+        .or_insert(Symbol::NumberValue(protocol_id as u64));
+    device_properties
+        .entry(PropertyKey::NumberKey(BIND_AUTOBIND))
+        .or_insert(Symbol::NumberValue(if autobind { 1 } else { 0 }));
+
+    let bytecode = unsafe { std::slice::from_raw_parts(bytecode_c, bytecode_sz).to_vec() };
+
     // TODO(fxb/73943): Return the error instead of returning false.
     match_bytecode(bytecode, device_properties).unwrap_or_else(|e| {
         println!("Error evaluating the bytecode: {}", e);