1 // Copyright 2021 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 data_model::{DataInit, Le16, Le32, Le64};
6 
7 pub const SECTOR_SHIFT: u8 = 9;
8 pub const SECTOR_SIZE: u64 = 0x01 << SECTOR_SHIFT;
9 pub const MAX_DISCARD_SECTORS: u32 = u32::MAX;
10 pub const MAX_WRITE_ZEROES_SECTORS: u32 = u32::MAX;
11 // Arbitrary limits for number of discard/write zeroes segments.
12 pub const MAX_DISCARD_SEG: u32 = 32;
13 pub const MAX_WRITE_ZEROES_SEG: u32 = 32;
14 // Hard-coded to 64 KiB (in 512-byte sectors) for now,
15 // but this should probably be based on cluster size for qcow.
16 pub const DISCARD_SECTOR_ALIGNMENT: u32 = 128;
17 
18 pub const ID_LEN: usize = 20;
19 
20 /// Virtio block device identifier.
21 /// This is an ASCII string terminated by a \0, unless all 20 bytes are used,
22 /// in which case the \0 terminator is omitted.
23 pub type BlockId = [u8; ID_LEN];
24 
25 pub const VIRTIO_BLK_T_IN: u32 = 0;
26 pub const VIRTIO_BLK_T_OUT: u32 = 1;
27 pub const VIRTIO_BLK_T_FLUSH: u32 = 4;
28 pub const VIRTIO_BLK_T_GET_ID: u32 = 8;
29 pub const VIRTIO_BLK_T_DISCARD: u32 = 11;
30 pub const VIRTIO_BLK_T_WRITE_ZEROES: u32 = 13;
31 
32 pub const VIRTIO_BLK_S_OK: u8 = 0;
33 pub const VIRTIO_BLK_S_IOERR: u8 = 1;
34 pub const VIRTIO_BLK_S_UNSUPP: u8 = 2;
35 
36 pub const VIRTIO_BLK_F_SEG_MAX: u32 = 2;
37 pub const VIRTIO_BLK_F_RO: u32 = 5;
38 pub const VIRTIO_BLK_F_BLK_SIZE: u32 = 6;
39 pub const VIRTIO_BLK_F_FLUSH: u32 = 9;
40 pub const VIRTIO_BLK_F_MQ: u32 = 12;
41 pub const VIRTIO_BLK_F_DISCARD: u32 = 13;
42 pub const VIRTIO_BLK_F_WRITE_ZEROES: u32 = 14;
43 
44 #[derive(Copy, Clone, Debug, Default)]
45 #[repr(C)]
46 pub struct virtio_blk_geometry {
47     cylinders: Le16,
48     heads: u8,
49     sectors: u8,
50 }
51 
52 // Safe because it only has data and has no implicit padding.
53 unsafe impl DataInit for virtio_blk_geometry {}
54 
55 #[derive(Copy, Clone, Debug, Default)]
56 #[repr(C)]
57 pub struct virtio_blk_topology {
58     physical_block_exp: u8,
59     alignment_offset: u8,
60     min_io_size: Le16,
61     opt_io_size: Le32,
62 }
63 
64 // Safe because it only has data and has no implicit padding.
65 unsafe impl DataInit for virtio_blk_topology {}
66 
67 #[derive(Copy, Clone, Debug, Default)]
68 #[repr(C, packed)]
69 pub struct virtio_blk_config {
70     pub capacity: Le64,
71     pub size_max: Le32,
72     pub seg_max: Le32,
73     pub geometry: virtio_blk_geometry,
74     pub blk_size: Le32,
75     pub topology: virtio_blk_topology,
76     pub writeback: u8,
77     pub unused0: u8,
78     pub num_queues: Le16,
79     pub max_discard_sectors: Le32,
80     pub max_discard_seg: Le32,
81     pub discard_sector_alignment: Le32,
82     pub max_write_zeroes_sectors: Le32,
83     pub max_write_zeroes_seg: Le32,
84     pub write_zeroes_may_unmap: u8,
85     pub unused1: [u8; 3],
86 }
87 
88 // Safe because it only has data and has no implicit padding.
89 unsafe impl DataInit for virtio_blk_config {}
90 
91 #[derive(Copy, Clone, Debug, Default)]
92 #[repr(C)]
93 pub(crate) struct virtio_blk_req_header {
94     pub req_type: Le32,
95     pub reserved: Le32,
96     pub sector: Le64,
97 }
98 
99 // Safe because it only has data and has no implicit padding.
100 unsafe impl DataInit for virtio_blk_req_header {}
101 
102 #[derive(Copy, Clone, Debug, Default)]
103 #[repr(C)]
104 pub(crate) struct virtio_blk_discard_write_zeroes {
105     pub sector: Le64,
106     pub num_sectors: Le32,
107     pub flags: Le32,
108 }
109 
110 pub(crate) const VIRTIO_BLK_DISCARD_WRITE_ZEROES_FLAG_UNMAP: u32 = 1 << 0;
111 
112 // Safe because it only has data and has no implicit padding.
113 unsafe impl DataInit for virtio_blk_discard_write_zeroes {}
114 
115 /// Builds and returns the config structure used to specify block features.
build_config_space( disk_size: u64, seg_max: u32, block_size: u32, num_queues: u16, ) -> virtio_blk_config116 pub fn build_config_space(
117     disk_size: u64,
118     seg_max: u32,
119     block_size: u32,
120     num_queues: u16,
121 ) -> virtio_blk_config {
122     virtio_blk_config {
123         // If the image is not a multiple of the sector size, the tail bits are not exposed.
124         capacity: Le64::from(disk_size >> SECTOR_SHIFT),
125         seg_max: Le32::from(seg_max),
126         blk_size: Le32::from(block_size),
127         num_queues: Le16::from(num_queues),
128         max_discard_sectors: Le32::from(MAX_DISCARD_SECTORS),
129         discard_sector_alignment: Le32::from(DISCARD_SECTOR_ALIGNMENT),
130         max_write_zeroes_sectors: Le32::from(MAX_WRITE_ZEROES_SECTORS),
131         write_zeroes_may_unmap: 1,
132         max_discard_seg: Le32::from(MAX_DISCARD_SEG),
133         max_write_zeroes_seg: Le32::from(MAX_WRITE_ZEROES_SEG),
134         ..Default::default()
135     }
136 }
137 
138 /// Returns the feature flags given the specified attributes.
build_avail_features( base_features: u64, read_only: bool, sparse: bool, multi_queue: bool, ) -> u64139 pub fn build_avail_features(
140     base_features: u64,
141     read_only: bool,
142     sparse: bool,
143     multi_queue: bool,
144 ) -> u64 {
145     let mut avail_features = base_features;
146     avail_features |= 1 << VIRTIO_BLK_F_FLUSH;
147     if read_only {
148         avail_features |= 1 << VIRTIO_BLK_F_RO;
149     } else {
150         if sparse {
151             avail_features |= 1 << VIRTIO_BLK_F_DISCARD;
152         }
153         avail_features |= 1 << VIRTIO_BLK_F_WRITE_ZEROES;
154     }
155     avail_features |= 1 << VIRTIO_BLK_F_SEG_MAX;
156     avail_features |= 1 << VIRTIO_BLK_F_BLK_SIZE;
157     if multi_queue {
158         avail_features |= 1 << VIRTIO_BLK_F_MQ;
159     }
160     avail_features
161 }
162