rutabaga_gfx: add better error reporting

Both RutabagaError::SpecViolation and RutabagaError::Unsupported
are used as catch-alls for errors that don't fit any one
particular place.

With this change, RutabagaError::Unsupported means that a particular
function is not implemented.

RutabagaError::SpecViolation(..) now provides free-form debug
reporting for one-off errors.  However, it is recommended to create a
new RutabagaError variant if two instances of the same spec violation
are noticed.  Many new variants are added in CL in observance of this
recommendation.

In addition, we may create a CrossDomainError and RutabagaGrallocError
one day.  Currently, it doesn't make sense to do so at the moment.
But prefix the new variants accordingly if the need arises.

BUG=b:173630595
TEST=compile and run

Change-Id: Ie610c77445f4406b40bb40f00bcabb8e65c8e02d
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3203231
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Gurchetan Singh <gurchetansingh@chromium.org>
Reviewed-by: Jason Macnak <natsu@google.com>
Reviewed-by: Keiichi Watanabe <keiichiw@chromium.org>
Reviewed-by: Dennis Kempin <denniskempin@google.com>
diff --git a/rutabaga_gfx/src/cross_domain/cross_domain.rs b/rutabaga_gfx/src/cross_domain/cross_domain.rs
index 4369e32..c4d8c25 100644
--- a/rutabaga_gfx/src/cross_domain/cross_domain.rs
+++ b/rutabaga_gfx/src/cross_domain/cross_domain.rs
@@ -146,10 +146,9 @@
             Ok(())
         }
         _ => {
-            // Expect to receive the write end of a Wayland pipe only.
             *descriptor_type = match FileFlags::from_file(descriptor) {
                 Ok(FileFlags::Write) => CROSS_DOMAIN_ID_TYPE_WRITE_PIPE,
-                _ => return Err(RutabagaError::SpecViolation),
+                _ => return Err(RutabagaError::InvalidCrossDomainItemType),
             };
             Ok(())
         }
@@ -272,7 +271,7 @@
     ) -> RutabagaResult<usize> {
         self.connection
             .as_ref()
-            .ok_or(RutabagaError::SpecViolation)
+            .ok_or(RutabagaError::InvalidCrossDomainChannel)
             .and_then(|conn| Ok(conn.send_with_fds(opaque_data, descriptors)?))
     }
 
@@ -294,7 +293,7 @@
 
             Ok((len, files))
         } else {
-            Err(RutabagaError::SpecViolation)
+            Err(RutabagaError::InvalidCrossDomainChannel)
         }
     }
 }
@@ -366,7 +365,7 @@
                                     &self.item_state,
                                     CrossDomainItem::WaylandWritePipe(file),
                                 ),
-                                _ => return Err(RutabagaError::SpecViolation),
+                                _ => return Err(RutabagaError::InvalidCrossDomainItemType),
                             };
                         }
 
@@ -402,7 +401,7 @@
                     let item = items
                         .table
                         .get_mut(&pipe_id)
-                        .ok_or(RutabagaError::SpecViolation)?;
+                        .ok_or(RutabagaError::InvalidCrossDomainItemId)?;
 
                     match item {
                         CrossDomainItem::WaylandReadPipe(ref mut file) => {
@@ -417,7 +416,7 @@
                                 self.wait_ctx.delete(file)?;
                             }
                         }
-                        _ => return Err(RutabagaError::SpecViolation),
+                        _ => return Err(RutabagaError::InvalidCrossDomainItemType),
                     }
 
                     if event.is_hungup && bytes_read == 0 {
@@ -459,13 +458,13 @@
                     let item = items
                         .table
                         .get(&read_pipe_id)
-                        .ok_or(RutabagaError::SpecViolation)?;
+                        .ok_or(RutabagaError::InvalidCrossDomainItemId)?;
 
                     match item {
                         CrossDomainItem::WaylandReadPipe(file) => self
                             .wait_ctx
                             .add(file, CrossDomainToken::WaylandReadPipe(read_pipe_id))?,
-                        _ => return Err(RutabagaError::SpecViolation),
+                        _ => return Err(RutabagaError::InvalidCrossDomainItemType),
                     }
                 }
             }
@@ -504,11 +503,14 @@
 
         // Zero means no requested channel.
         if cmd_init.channel_type != 0 {
-            let channels = self.channels.take().ok_or(RutabagaError::SpecViolation)?;
+            let channels = self
+                .channels
+                .take()
+                .ok_or(RutabagaError::InvalidCrossDomainChannel)?;
             let base_channel = &channels
                 .iter()
                 .find(|channel| channel.channel_type == cmd_init.channel_type)
-                .ok_or(RutabagaError::SpecViolation)?
+                .ok_or(RutabagaError::InvalidCrossDomainChannel)?
                 .base_channel;
 
             let connection = UnixStream::connect(base_channel)?;
@@ -591,7 +593,7 @@
             state.write_to_ring(RingWrite::Write(response, None))?;
             Ok(())
         } else {
-            Err(RutabagaError::SpecViolation)
+            Err(RutabagaError::InvalidCrossDomainState)
         }
     }
 
@@ -608,7 +610,9 @@
         let num_identifiers = cmd_send.num_identifiers.try_into()?;
 
         if num_identifiers > CROSS_DOMAIN_MAX_IDENTIFIERS {
-            return Err(RutabagaError::SpecViolation);
+            return Err(RutabagaError::SpecViolation(
+                "max cross domain identifiers exceeded",
+            ));
         }
 
         let iter = cmd_send
@@ -629,13 +633,13 @@
                 if let Some(ref handle) = context_resource.handle {
                     *descriptor = handle.os_handle.as_raw_descriptor();
                 } else {
-                    return Err(RutabagaError::SpecViolation);
+                    return Err(RutabagaError::InvalidRutabagaHandle);
                 }
             } else if *identifier_type == CROSS_DOMAIN_ID_TYPE_READ_PIPE {
-                // In practice, just 1 write or read pipe per send is observed.  If we encounter
+                // In practice, just 1 pipe pair per send is observed.  If we encounter
                 // more, this can be changed later.
                 if write_pipe_opt.is_some() {
-                    return Err(RutabagaError::SpecViolation);
+                    return Err(RutabagaError::SpecViolation("expected just one pipe pair"));
                 }
 
                 let (read_pipe, write_pipe) = pipe(true)?;
@@ -651,7 +655,7 @@
                 // because of the way Sommelier copy + paste works.  If the Sommelier sequence of events
                 // changes, it's always possible to wait for the host response.
                 if read_pipe_id != *identifier {
-                    return Err(RutabagaError::SpecViolation);
+                    return Err(RutabagaError::InvalidCrossDomainItemId);
                 }
 
                 // The write pipe needs to be dropped after the send_msg(..) call is complete, so the read pipe
@@ -660,7 +664,7 @@
                 read_pipe_id_opt = Some(read_pipe_id);
             } else {
                 // Don't know how to handle anything else yet.
-                return Err(RutabagaError::SpecViolation);
+                return Err(RutabagaError::InvalidCrossDomainItemType);
             }
         }
 
@@ -672,7 +676,7 @@
                 resample_evt.write(1)?;
             }
         } else {
-            return Err(RutabagaError::SpecViolation);
+            return Err(RutabagaError::InvalidCrossDomainState);
         }
 
         Ok(())
@@ -691,7 +695,7 @@
         let item = items
             .table
             .remove(&cmd_write.identifier)
-            .ok_or(RutabagaError::SpecViolation)?;
+            .ok_or(RutabagaError::InvalidCrossDomainItemId)?;
 
         let len: usize = cmd_write.opaque_data_size.try_into()?;
         match item {
@@ -709,7 +713,7 @@
 
                 Ok(())
             }
-            _ => Err(RutabagaError::SpecViolation),
+            _ => Err(RutabagaError::InvalidCrossDomainItemType),
         }
     }
 }
@@ -755,12 +759,12 @@
             let item = items
                 .table
                 .get(&item_id)
-                .ok_or(RutabagaError::SpecViolation)?;
+                .ok_or(RutabagaError::InvalidCrossDomainItemId)?;
 
             match item {
                 CrossDomainItem::ImageRequirements(reqs) => {
                     if reqs.size != resource_create_blob.size {
-                        return Err(RutabagaError::SpecViolation);
+                        return Err(RutabagaError::SpecViolation("blob size mismatch"));
                     }
 
                     // Strictly speaking, it's against the virtio-gpu spec to allocate memory in the context
@@ -794,7 +798,7 @@
                         backing_iovecs: None,
                     })
                 }
-                _ => Err(RutabagaError::SpecViolation),
+                _ => Err(RutabagaError::InvalidCrossDomainItemType),
             }
         } else {
             let item = self
@@ -802,7 +806,7 @@
                 .lock()
                 .table
                 .remove(&item_id)
-                .ok_or(RutabagaError::SpecViolation)?;
+                .ok_or(RutabagaError::InvalidCrossDomainItemId)?;
 
             match item {
                 CrossDomainItem::WaylandKeymap(descriptor) => {
@@ -824,7 +828,7 @@
                         backing_iovecs: None,
                     })
                 }
-                _ => Err(RutabagaError::SpecViolation),
+                _ => Err(RutabagaError::InvalidCrossDomainItemType),
             }
         }
     }
@@ -870,7 +874,7 @@
 
                     self.write(&cmd_write, opaque_data)?;
                 }
-                _ => return Err(RutabagaError::Unsupported),
+                _ => return Err(RutabagaError::SpecViolation("invalid cross domain command")),
             }
 
             offset += hdr.cmd_size as usize;
@@ -911,7 +915,7 @@
                     state.add_job(CrossDomainJob::HandleFence(fence));
                 }
             }
-            _ => return Err(RutabagaError::SpecViolation),
+            _ => return Err(RutabagaError::SpecViolation("unexpected ring type")),
         }
 
         Ok(())
@@ -954,7 +958,9 @@
         if resource_create_blob.blob_mem != RUTABAGA_BLOB_MEM_GUEST
             && resource_create_blob.blob_flags != RUTABAGA_BLOB_FLAG_USE_MAPPABLE
         {
-            return Err(RutabagaError::SpecViolation);
+            return Err(RutabagaError::SpecViolation(
+                "expected only guest memory blobs",
+            ));
         }
 
         Ok(RutabagaResource {
diff --git a/rutabaga_gfx/src/rutabaga_2d.rs b/rutabaga_gfx/src/rutabaga_2d.rs
index acfbc82..bf5ee91 100644
--- a/rutabaga_gfx/src/rutabaga_2d.rs
+++ b/rutabaga_gfx/src/rutabaga_2d.rs
@@ -192,12 +192,15 @@
             return Ok(());
         }
 
-        let mut info_2d = resource.info_2d.take().ok_or(RutabagaError::Unsupported)?;
+        let mut info_2d = resource
+            .info_2d
+            .take()
+            .ok_or(RutabagaError::Invalid2DInfo)?;
 
         let iovecs = resource
             .backing_iovecs
             .take()
-            .ok_or(RutabagaError::Unsupported)?;
+            .ok_or(RutabagaError::InvalidIovec)?;
 
         // All offical virtio_gpu formats are 4 bytes per pixel.
         let resource_bpp = 4;
@@ -241,7 +244,10 @@
         transfer: Transfer3D,
         buf: Option<VolatileSlice>,
     ) -> RutabagaResult<()> {
-        let mut info_2d = resource.info_2d.take().ok_or(RutabagaError::Unsupported)?;
+        let mut info_2d = resource
+            .info_2d
+            .take()
+            .ok_or(RutabagaError::Invalid2DInfo)?;
 
         // All offical virtio_gpu formats are 4 bytes per pixel.
         let resource_bpp = 4;
@@ -249,7 +255,9 @@
         let src_offset = 0;
         let dst_offset = 0;
 
-        let dst_slice = buf.ok_or(RutabagaError::Unsupported)?;
+        let dst_slice = buf.ok_or(RutabagaError::SpecViolation(
+            "need a destination slice for transfer read",
+        ))?;
 
         transfer_2d(
             info_2d.width,
diff --git a/rutabaga_gfx/src/rutabaga_core.rs b/rutabaga_gfx/src/rutabaga_core.rs
index 46175f0..f5cd56d 100644
--- a/rutabaga_gfx/src/rutabaga_core.rs
+++ b/rutabaga_gfx/src/rutabaga_core.rs
@@ -507,7 +507,9 @@
             .get(&resource_id)
             .ok_or(RutabagaError::InvalidResourceId)?;
 
-        resource.map_info.ok_or(RutabagaError::SpecViolation)
+        resource
+            .map_info
+            .ok_or(RutabagaError::SpecViolation("no map info available"))
     }
 
     /// Returns the `vulkan_info` of the blob resource, which consists of the physical device
@@ -518,7 +520,7 @@
             .get(&resource_id)
             .ok_or(RutabagaError::InvalidResourceId)?;
 
-        resource.vulkan_info.ok_or(RutabagaError::Unsupported)
+        resource.vulkan_info.ok_or(RutabagaError::InvalidVulkanInfo)
     }
 
     /// Returns the 3D info associated with the resource, if any.
@@ -528,7 +530,9 @@
             .get(&resource_id)
             .ok_or(RutabagaError::InvalidResourceId)?;
 
-        resource.info_3d.ok_or(RutabagaError::Unsupported)
+        resource
+            .info_3d
+            .ok_or(RutabagaError::SpecViolation("no 3d info available"))
     }
 
     /// Exports a blob resource.  See virtio-gpu spec for blob flag use flags.
@@ -552,10 +556,11 @@
             }
             (Some(handle), false) => {
                 // Exactly one strong reference in this case.
-                let hnd = Arc::try_unwrap(handle).map_err(|_| RutabagaError::SpecViolation)?;
+                let hnd =
+                    Arc::try_unwrap(handle).map_err(|_| RutabagaError::InvalidRutabagaHandle)?;
                 Ok(hnd)
             }
-            _ => Err(RutabagaError::Unsupported),
+            _ => Err(RutabagaError::InvalidRutabagaHandle),
         }
     }
 
diff --git a/rutabaga_gfx/src/rutabaga_gralloc/formats.rs b/rutabaga_gfx/src/rutabaga_gralloc/formats.rs
index 8803b5b..6dd0ac6 100644
--- a/rutabaga_gfx/src/rutabaga_gralloc/formats.rs
+++ b/rutabaga_gfx/src/rutabaga_gralloc/formats.rs
@@ -136,7 +136,7 @@
             DRM_FORMAT_ABGR16161616F => Ok(PACKED_8BPP),
             DRM_FORMAT_NV12 => Ok(BIPLANAR_YUV420),
             DRM_FORMAT_YVU420 => Ok(TRIPLANAR_YUV420),
-            _ => Err(RutabagaError::Unsupported),
+            _ => Err(RutabagaError::InvalidGrallocDrmFormat),
         }
     }
 
@@ -158,7 +158,7 @@
             DRM_FORMAT_ABGR16161616F => Ok(VulkanFormat::R16G16B16A16Sfloat),
             DRM_FORMAT_NV12 => Ok(VulkanFormat::G8B8R8_2PLANE420Unorm),
             DRM_FORMAT_YVU420 => Ok(VulkanFormat::G8B8R8_3PLANE420Unorm),
-            _ => Err(RutabagaError::Unsupported),
+            _ => Err(RutabagaError::InvalidGrallocDrmFormat),
         }
     }
 
@@ -180,15 +180,15 @@
             DRM_FORMAT_NV12 => match plane {
                 0 => Ok(VulkanImageAspect::Plane0),
                 1 => Ok(VulkanImageAspect::Plane1),
-                _ => Err(RutabagaError::Unsupported),
+                _ => Err(RutabagaError::InvalidGrallocNumberOfPlanes),
             },
             DRM_FORMAT_YVU420 => match plane {
                 0 => Ok(VulkanImageAspect::Plane0),
                 1 => Ok(VulkanImageAspect::Plane1),
                 2 => Ok(VulkanImageAspect::Plane2),
-                _ => Err(RutabagaError::Unsupported),
+                _ => Err(RutabagaError::InvalidGrallocNumberOfPlanes),
             },
-            _ => Err(RutabagaError::Unsupported),
+            _ => Err(RutabagaError::InvalidGrallocDrmFormat),
         }
     }
 }
diff --git a/rutabaga_gfx/src/rutabaga_gralloc/gralloc.rs b/rutabaga_gfx/src/rutabaga_gralloc/gralloc.rs
index 9b3f25f..d950c4f 100644
--- a/rutabaga_gfx/src/rutabaga_gralloc/gralloc.rs
+++ b/rutabaga_gfx/src/rutabaga_gralloc/gralloc.rs
@@ -303,7 +303,7 @@
         let gralloc = self
             .grallocs
             .get_mut(&backend)
-            .ok_or(RutabagaError::Unsupported)?;
+            .ok_or(RutabagaError::InvalidGrallocBackend)?;
 
         let mut reqs = gralloc.get_image_memory_requirements(info)?;
         reqs.size = round_up_to_page_size(reqs.size as usize) as u64;
@@ -320,7 +320,7 @@
         let gralloc = self
             .grallocs
             .get_mut(&backend)
-            .ok_or(RutabagaError::Unsupported)?;
+            .ok_or(RutabagaError::InvalidGrallocBackend)?;
 
         gralloc.allocate_memory(reqs)
     }
@@ -336,7 +336,7 @@
         let gralloc = self
             .grallocs
             .get_mut(&GrallocBackend::Vulkano)
-            .ok_or(RutabagaError::Unsupported)?;
+            .ok_or(RutabagaError::InvalidGrallocBackend)?;
 
         gralloc.import_and_map(handle, vulkan_info, size)
     }
diff --git a/rutabaga_gfx/src/rutabaga_gralloc/minigbm.rs b/rutabaga_gfx/src/rutabaga_gralloc/minigbm.rs
index e4d4b05..c86e7cf 100644
--- a/rutabaga_gfx/src/rutabaga_gralloc/minigbm.rs
+++ b/rutabaga_gfx/src/rutabaga_gralloc/minigbm.rs
@@ -140,7 +140,7 @@
                 || gbm_buffer.height() != reqs.info.height
                 || gbm_buffer.format() != reqs.info.drm_format
             {
-                return Err(RutabagaError::SpecViolation);
+                return Err(RutabagaError::InvalidGrallocDimensions);
             }
 
             let dmabuf = gbm_buffer.export()?.into();
diff --git a/rutabaga_gfx/src/rutabaga_gralloc/rendernode.rs b/rutabaga_gfx/src/rutabaga_gralloc/rendernode.rs
index 9972ab5..d7fe1d3 100644
--- a/rutabaga_gfx/src/rutabaga_gralloc/rendernode.rs
+++ b/rutabaga_gfx/src/rutabaga_gralloc/rendernode.rs
@@ -105,5 +105,5 @@
         }
     }
 
-    Err(RutabagaError::Unsupported)
+    Err(RutabagaError::SpecViolation("no DRM rendernode opened"))
 }
diff --git a/rutabaga_gfx/src/rutabaga_gralloc/vulkano_gralloc.rs b/rutabaga_gfx/src/rutabaga_gralloc/vulkano_gralloc.rs
index b0912db..00a4327 100644
--- a/rutabaga_gfx/src/rutabaga_gralloc/vulkano_gralloc.rs
+++ b/rutabaga_gfx/src/rutabaga_gralloc/vulkano_gralloc.rs
@@ -82,7 +82,9 @@
                     // We take the first queue family that supports graphics.
                     q.supports_graphics()
                 })
-                .ok_or(RutabagaError::Unsupported)?;
+                .ok_or(RutabagaError::SpecViolation(
+                    "need graphics queue family to proceed",
+                ))?;
 
             let supported_extensions = DeviceExtensions::supported_by_device(physical);
 
@@ -135,11 +137,11 @@
         let device = if self.has_integrated_gpu {
             self.devices
                 .get(&PhysicalDeviceType::IntegratedGpu)
-                .ok_or(RutabagaError::Unsupported)?
+                .ok_or(RutabagaError::InvalidGrallocGpuType)?
         } else {
             self.devices
                 .get(&PhysicalDeviceType::DiscreteGpu)
-                .ok_or(RutabagaError::Unsupported)?
+                .ok_or(RutabagaError::InvalidGrallocGpuType)?
         };
 
         let usage = match info.flags.uses_rendering() {
@@ -155,12 +157,12 @@
 
         // Reasonable bounds on image width.
         if info.width == 0 || info.width > 4096 {
-            return Err(RutabagaError::SpecViolation);
+            return Err(RutabagaError::InvalidGrallocDimensions);
         }
 
         // Reasonable bounds on image height.
         if info.height == 0 || info.height > 4096 {
-            return Err(RutabagaError::SpecViolation);
+            return Err(RutabagaError::InvalidGrallocDimensions);
         }
 
         let vulkan_format = info.drm_format.vulkan_format()?;
@@ -217,11 +219,11 @@
         let device = if self.has_integrated_gpu {
             self.devices
                 .get(&PhysicalDeviceType::IntegratedGpu)
-                .ok_or(RutabagaError::Unsupported)?
+                .ok_or(RutabagaError::InvalidGrallocGpuType)?
         } else {
             self.devices
                 .get(&PhysicalDeviceType::DiscreteGpu)
-                .ok_or(RutabagaError::Unsupported)?
+                .ok_or(RutabagaError::InvalidGrallocGpuType)?
         };
 
         let planar_layout = info.drm_format.planar_layout()?;
@@ -276,7 +278,9 @@
                 .chain(second_loop)
                 .filter(|&(t, _)| (memory_requirements.memory_type_bits & (1 << t.id())) != 0)
                 .find(|&(t, rq)| filter(t) == rq)
-                .ok_or(RutabagaError::Unsupported)?
+                .ok_or(RutabagaError::SpecViolation(
+                    "unable to find required memory type",
+                ))?
                 .0
         };
 
@@ -302,22 +306,22 @@
     fn allocate_memory(&mut self, reqs: ImageMemoryRequirements) -> RutabagaResult<RutabagaHandle> {
         let (unsafe_image, memory_requirements) = unsafe { self.create_image(reqs.info)? };
 
-        let vulkan_info = reqs.vulkan_info.ok_or(RutabagaError::SpecViolation)?;
+        let vulkan_info = reqs.vulkan_info.ok_or(RutabagaError::InvalidVulkanInfo)?;
 
         let device = if self.has_integrated_gpu {
             self.devices
                 .get(&PhysicalDeviceType::IntegratedGpu)
-                .ok_or(RutabagaError::Unsupported)?
+                .ok_or(RutabagaError::InvalidGrallocGpuType)?
         } else {
             self.devices
                 .get(&PhysicalDeviceType::DiscreteGpu)
-                .ok_or(RutabagaError::Unsupported)?
+                .ok_or(RutabagaError::InvalidGrallocGpuType)?
         };
 
         let memory_type = device
             .physical_device()
             .memory_type_by_id(vulkan_info.memory_idx)
-            .ok_or(RutabagaError::SpecViolation)?;
+            .ok_or(RutabagaError::InvalidVulkanInfo)?;
 
         let (handle_type, rutabaga_type) =
             match device.enabled_extensions().ext_external_memory_dma_buf {
@@ -374,7 +378,7 @@
             .find(|device| {
                 device.physical_device().index() as u32 == vulkan_info.physical_device_idx
             })
-            .ok_or(RutabagaError::Unsupported)?;
+            .ok_or(RutabagaError::InvalidVulkanInfo)?;
 
         let handle_type = match handle.handle_type {
             RUTABAGA_MEM_HANDLE_TYPE_DMABUF => ExternalMemoryHandleType {
@@ -385,7 +389,7 @@
                 opaque_fd: true,
                 ..ExternalMemoryHandleType::none()
             },
-            _ => return Err(RutabagaError::Unsupported),
+            _ => return Err(RutabagaError::InvalidRutabagaHandle),
         };
 
         let device_memory = DeviceMemoryBuilder::new(device.clone(), vulkan_info.memory_idx, size)
diff --git a/rutabaga_gfx/src/rutabaga_utils.rs b/rutabaga_gfx/src/rutabaga_utils.rs
index 9e7aebe..685d3b1 100644
--- a/rutabaga_gfx/src/rutabaga_utils.rs
+++ b/rutabaga_gfx/src/rutabaga_utils.rs
@@ -145,9 +145,9 @@
     /// An internal Rutabaga component error was returned.
     #[error("rutabaga component failed with error {0}")]
     ComponentError(i32),
-    /// The Rutabaga component failed to export a RutabagaHandle.
-    #[error("failed to export Rutabaga handle")]
-    ExportedRutabagaHandle,
+    /// Invalid 2D info
+    #[error("invalid 2D info")]
+    Invalid2DInfo,
     /// Invalid Capset
     #[error("invalid capset")]
     InvalidCapset,
@@ -160,6 +160,33 @@
     /// Invalid Context ID
     #[error("invalid context id")]
     InvalidContextId,
+    /// Invalid cross domain channel
+    #[error("invalid cross domain channel")]
+    InvalidCrossDomainChannel,
+    /// Invalid cross domain item ID
+    #[error("invalid cross domain item id")]
+    InvalidCrossDomainItemId,
+    /// Invalid cross domain item type
+    #[error("invalid cross domain item type")]
+    InvalidCrossDomainItemType,
+    /// Invalid cross domain state
+    #[error("invalid cross domain state")]
+    InvalidCrossDomainState,
+    /// Invalid gralloc backend.
+    #[error("invalid gralloc backend")]
+    InvalidGrallocBackend,
+    /// Invalid gralloc dimensions.
+    #[error("invalid gralloc dimensions")]
+    InvalidGrallocDimensions,
+    /// Invalid gralloc DRM format.
+    #[error("invalid gralloc DRM format")]
+    InvalidGrallocDrmFormat,
+    /// Invalid GPU type.
+    #[error("invalid GPU type for gralloc")]
+    InvalidGrallocGpuType,
+    /// Invalid number of YUV planes.
+    #[error("invalid number of YUV planes")]
+    InvalidGrallocNumberOfPlanes,
     /// The indicated region of guest memory is invalid.
     #[error("an iovec is outside of guest memory's range")]
     InvalidIovec,
@@ -169,6 +196,12 @@
     /// Indicates an error in the RutabagaBuilder.
     #[error("invalid rutabaga build parameters")]
     InvalidRutabagaBuild,
+    /// An error with the RutabagaHandle
+    #[error("invalid rutabaga handle")]
+    InvalidRutabagaHandle,
+    /// Invalid Vulkan info
+    #[error("invalid vulkan info")]
+    InvalidVulkanInfo,
     /// An input/output error occured.
     #[error("an input/output error occur: {0}")]
     IoError(IoError),
@@ -176,13 +209,13 @@
     #[error("The mapping failed for the following reason: {0}")]
     MappingFailed(ExternalMappingError),
     /// Violation of the Rutabaga spec occured.
-    #[error("violation of the rutabaga spec")]
-    SpecViolation,
+    #[error("violation of the rutabaga spec: {0}")]
+    SpecViolation(&'static str),
     /// An attempted integer conversion failed.
     #[error("int conversion failed: {0}")]
     TryFromIntError(TryFromIntError),
     /// The command is unsupported.
-    #[error("feature or function unsupported")]
+    #[error("the requested function is not implemented")]
     Unsupported,
     /// Utf8 error.
     #[error("an utf8 error occured: {0}")]
@@ -484,7 +517,7 @@
         let clone = self
             .os_handle
             .try_clone()
-            .map_err(|_| RutabagaError::Unsupported)?;
+            .map_err(|_| RutabagaError::InvalidRutabagaHandle)?;
         Ok(RutabagaHandle {
             os_handle: clone,
             handle_type: self.handle_type,