Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 1 | #!/bin/sh |
| 2 | # Copyright 2014 The Chromium OS Authors. All rights reserved. |
| 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | |
| 6 | # This runs from the factory install/reset shim. This MUST be run |
| 7 | # from USB, in developer mode. This script contains functions to erase |
| 8 | # securly the disk and verify it has been erased properly. |
| 9 | |
| 10 | # if used inside the test harness, 2 variables are defined: |
Thiemo Nagel | eb49427 | 2019-09-04 12:20:08 +0200 | [diff] [blame] | 11 | # TEST_DELAY: to reduce the amount of time checking for the eMMC to be ready |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 12 | # again, |
| 13 | # TEST_FIO_OUTPUT: to send the output of fio to a known file. By default, |
| 14 | # if the file is generated with $(mktemp fio_output_XXXXXX) |
| 15 | |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 16 | . /usr/share/misc/chromeos-common.sh |
| 17 | |
| 18 | # Return value for partial success when strict is set. |
| 19 | STRICT_RETURN_VALUE=100 |
| 20 | |
| 21 | |
| 22 | # Outputs the NVMe namespace block devices that belong to the given |
| 23 | # character device. |
| 24 | # Arguments: |
| 25 | # - NVMe character device without preceding /dev/ (ex: nvme0) |
| 26 | # Outputs NVMe namespaces' block devices (ex: /dev/nvme0n1) |
| 27 | get_nvme_block_devs() { |
| 28 | local nvme="$1" |
| 29 | local block_dev |
| 30 | for block_dev in /sys/block/${nvme}*; do |
| 31 | echo "/dev/${block_dev##*/}" |
| 32 | done |
| 33 | } |
| 34 | |
| 35 | # Outputs the size of given NVMe character device |
| 36 | # Arguments: |
| 37 | # - NVMe character device (ex: /dev/nvme0) |
| 38 | get_nvme_char_device_size() { |
| 39 | local disk="$1" |
| 40 | local dev_size="$(nvme id-ctrl --output-format=json "${dev}" | jq ".tnvmcap")" |
| 41 | if [ ${dev_size} -eq 0 ]; then |
| 42 | # This may not be exact size of the disk because there might be some |
| 43 | # unused blocks. |
| 44 | local blk blk_size |
| 45 | for blk in $(get_nvme_block_devs "${disk##*/}"); do |
| 46 | blk_size="$(blockdev --getsize64 ${blk})" |
| 47 | : $(( dev_size += blk_size )) |
| 48 | done |
| 49 | fi |
| 50 | echo "${dev_size}" |
| 51 | } |
| 52 | |
| 53 | # Get the supported erase mode for ATA disk. |
| 54 | # Arguments: |
| 55 | # - ATA device to query |
| 56 | # Outputs supported erase mode: "--security-erase" or |
| 57 | # "--security-erase-enhanced", empty string if not supported or unknown. |
| 58 | get_ata_supported_erase_mode() { |
| 59 | local disk="$1" |
| 60 | local func |
| 61 | local supported_mode |
| 62 | for func in "supported" "supported: enhanced erase"; do |
| 63 | hdparm -I "${disk}" \ |
| 64 | | grep -A10 "^Security" \ |
| 65 | | grep -q "^[[:blank:]]\+${func}$" |
| 66 | if [ $? -eq 0 ]; then |
| 67 | if [ "${func}" = "supported" ]; then |
| 68 | supported_mode="--security-erase" |
| 69 | else |
| 70 | supported_mode="--security-erase-enhanced" |
| 71 | fi |
| 72 | fi |
| 73 | done |
| 74 | echo "${supported_mode}" |
| 75 | } |
| 76 | |
| 77 | # Check if the ATA device supports block erase sanitize. |
| 78 | # Arguments: |
| 79 | # - device to query |
| 80 | # Return true if the operation is supported, false if not supported. |
| 81 | is_sata_sanitize_supported() { |
| 82 | local dev="$1" |
| 83 | hdparm -I "${dev}" | grep -q "BLOCK_ERASE_EXT command" |
| 84 | return $? |
| 85 | } |
| 86 | |
| 87 | # Check if the most recent sanitize operation was completed successfully |
| 88 | # Arguments: |
| 89 | # - device to query |
| 90 | # This is blocking until the sanitize has finished. Return true if the most |
| 91 | # recent sanitize operation was successful, false otherwise. |
| 92 | is_sata_sanitize_successful() { |
| 93 | local dev="$1" |
| 94 | hdparm --sanitize-status "${disk}" \ |
| 95 | | grep -iq "Last Sanitize Operation Completed Without Error" |
| 96 | } |
| 97 | |
| 98 | # Check if the NVMe device supports block erase sanitize. |
| 99 | # Arguments: |
| 100 | # - device to query (block device or character device) |
| 101 | # Return true if the operation is supported, false if not supported. |
| 102 | is_nvme_sanitize_supported() { |
| 103 | local dev="$1" |
| 104 | # Check bit #1. If it is set to 1, device supports block erase sanitize. |
| 105 | local sanicap=$(nvme id-ctrl --output-format=json "${dev}" \ |
| 106 | | jq ".sanicap") |
| 107 | test $(( sanicap / 2 % 2 )) -eq 1 |
| 108 | return $? |
| 109 | } |
| 110 | |
| 111 | # Check if the device is currently being sanitized. |
| 112 | # Arguments: |
| 113 | # - device to query |
| 114 | # Return true if is being sanitized, false otherwise. |
| 115 | is_nvme_sanitize_in_progress() { |
| 116 | local dev="$1" |
| 117 | # Check last 3 bits of the sanitize status value. If it is set to 010, the |
| 118 | # sanitize operation is in progress. |
| 119 | local status=$(nvme sanitize-log "${dev}" \ |
| 120 | | grep "(SSTAT)" \ |
| 121 | | grep -oEi "(0x)?[[:xdigit:]]+$") |
| 122 | test $(( status % 8 )) -eq 2 |
| 123 | return $? |
| 124 | } |
| 125 | |
| 126 | # Check if the most recent sanitize operation was completed successfully |
| 127 | # Arguments: |
| 128 | # - device to query |
| 129 | # Return true if the most recent sanitize operation was successful, false |
| 130 | # otherwise. |
| 131 | is_nvme_sanitize_successful() { |
| 132 | local dev="$1" |
| 133 | # Check last 3 bits of the sanitize status value. If it is set to 001, |
| 134 | # sanitize operation was successful. |
| 135 | local status=$(nvme sanitize-log "${dev}" \ |
| 136 | | grep "(SSTAT)" \ |
| 137 | | grep -oEi "(0x)?[[:xdigit:]]+$") |
| 138 | test $(( status % 8 )) -eq 1 |
| 139 | return $? |
| 140 | } |
| 141 | |
| 142 | # Output the progress of nvme sanitize command. This value indicates the |
| 143 | # fraction complete of the sanitize operation. The value is the numerator of |
| 144 | # the fraction complete that has 65536 as its denominator. This value is set |
| 145 | # to FFFFh (65536) if there is no sanitize in progress. |
| 146 | # Arguments: |
| 147 | # - device to query |
| 148 | get_nvme_sanitize_progress() { |
| 149 | local dev="$1" |
| 150 | nvme sanitize-log "${dev}" | grep "(SPROG)" | grep -oEi "[[:xdigit:]]+$" |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 151 | } |
| 152 | |
| 153 | # Return useful bits of the MMC status |
| 154 | # |
| 155 | # Return some status bits that indicates if the device has completed |
| 156 | # outstanding commands. |
| 157 | # |
| 158 | # if 'mmc status get' fails returns 0, which is an invalid status. |
| 159 | get_mmc_status() { |
| 160 | local status |
| 161 | status=$(mmc status get "$1" | sed -nre 's/^SEND_STATUS response: (.*)/\1/p') |
| 162 | # The state is defined in chapter 6.13 of eMMC rev 5. |
| 163 | # Ideally, we should check that all the error bits to be set to 0. |
| 164 | # Now, reading in more details, eMMC device are not garantee to be always |
| 165 | # valid (see X or R mode) or some bit are reserved. |
| 166 | # Therefore, we limit only to flags that are always valid: |
| 167 | # bit 6: EXCEPTION_EVENT: set to 0 |
Thiemo Nagel | eb49427 | 2019-09-04 12:20:08 +0200 | [diff] [blame] | 168 | # bit 8: READY_FOR_DATA: set to 1 (0 while sanitizing) |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 169 | # bit 9-12: CURRENT_STATE: set to 4 (Tran), set to 7 (Prg while sanitizing) |
| 170 | printf "0x%08x\n" $((status & 0x00001F40)) |
| 171 | } |
| 172 | |
Thiemo Nagel | eb49427 | 2019-09-04 12:20:08 +0200 | [diff] [blame] | 173 | # Erase an MMC device using firmware functions |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 174 | # |
| 175 | # Ask the device to trim all sectors. |
| 176 | # Then, ask the device to physically erase all trimmed sectors. |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 177 | # Arguments: |
| 178 | # - block device to erase |
| 179 | # - strict: if set to "1", then only returns success if sanitize successfully |
| 180 | # physically erases the device when security is supported. If left |
| 181 | # empty or set to "0", returns success when sanitize succeeds |
| 182 | # whether security is supported or not. |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 183 | secure_erase_mmc() { |
| 184 | local disk="$1" |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 185 | local strict="$2" |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 186 | local delay=${TEST_DELAY:-5} |
| 187 | local count |
| 188 | local secure |
| 189 | local rc |
| 190 | |
Thiemo Nagel | eb49427 | 2019-09-04 12:20:08 +0200 | [diff] [blame] | 191 | # Mark all location as unused -- try secure first. |
| 192 | for secure in "-s" ""; do |
| 193 | blkdiscard ${secure} "${disk}" |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 194 | rc=$? |
| 195 | if [ ${rc} -eq 0 ]; then |
| 196 | break |
| 197 | fi |
| 198 | done |
| 199 | if [ ${rc} -ne 0 ]; then |
| 200 | echo "security not supported, just doing overwrite" |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 201 | fi |
| 202 | |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 203 | # Physically erase unused locations. |
Thiemo Nagel | eb49427 | 2019-09-04 12:20:08 +0200 | [diff] [blame] | 204 | # 0x00000900 equals to READY_FOR_DATA=1 and CURRENT_STATE=4 (Tran) |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 205 | mmc_orig_status=$(get_mmc_status "${disk}") |
| 206 | if [ "${mmc_orig_status}" != "0x00000900" ]; then |
| 207 | echo "Not ready for sanitize: status ${mmc_orig_status}." |
| 208 | return 1 |
| 209 | fi |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 210 | |
| 211 | mmc sanitize "${disk}" || return $? |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 212 | |
| 213 | count=120 # wait up to 10 minutes |
| 214 | mmc_status="0xffffffff" |
| 215 | while [ "${mmc_status}" != "${mmc_orig_status}" -a ${count} -gt 0 ]; do |
| 216 | sleep "${delay}" |
| 217 | mmc_status=$(get_mmc_status "${disk}") |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 218 | : $(( count -= 1 )) |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 219 | done |
| 220 | |
| 221 | if [ "${mmc_status}" != "${mmc_orig_status}" ]; then |
| 222 | echo "Device is stuck sanitizing: status ${mmc_status}." |
| 223 | return 1 |
| 224 | fi |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 225 | |
| 226 | if [ "${strict}" = "1" ] && [ ${rc} -ne 0 ]; then |
| 227 | return ${STRICT_RETURN_VALUE} |
| 228 | else |
| 229 | return 0 |
| 230 | fi |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 231 | } |
| 232 | |
| 233 | # Erase an ATA device using internal firmware function |
| 234 | # |
| 235 | # To trigger the ATA SECURE ERASE function, the disk must be |
| 236 | # in security mode SEC4 (aka locked) or SEC5 (aka secured). |
| 237 | # Disks are usually in SEC1 (unsecured). |
| 238 | # First put the disk in SEC5 then Erase it, that put it back in SEC1. |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 239 | # Arguments: |
| 240 | # - block device to erase |
| 241 | # - strict: if set to "1", then only returns success if enhanced security |
| 242 | # erase is supported and succeeds. Security-erase does not guarantee |
| 243 | # to erase unallocated sectors whereas enhanced security erase does. |
| 244 | # If left empty or set to "0", returns success if any of the |
| 245 | # security erase succeed. |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 246 | secure_erase_sata() { |
| 247 | local disk="$1" |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 248 | local strict="$2" |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 249 | local temp_password="chromeos" |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 250 | local erase_mode="$(get_ata_supported_erase_mode "${disk}")" |
| 251 | local partial_success_return_val=0 |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 252 | |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 253 | if [ "${strict}" = "1" ]; then |
| 254 | partial_success_return_val=${STRICT_RETURN_VALUE} |
| 255 | fi |
| 256 | |
| 257 | if is_sata_sanitize_supported "${disk}"; then |
| 258 | hdparm --yes-i-know-what-i-am-doing --sanitize-block-erase "${disk}" |
| 259 | is_sata_sanitize_successful "${disk}" && return 0 |
| 260 | fi |
| 261 | |
| 262 | if [ -n "${erase_mode}" ]; then |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 263 | hdparm --user-master u --security-set-pass \ |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 264 | "${temp_password}" "${disk}" || return $? |
| 265 | hdparm --user-master u "${erase_mode}" \ |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 266 | "${temp_password}" "${disk}" || return $? |
| 267 | else |
| 268 | echo "security not supported, just doing overwrite" |
| 269 | fi |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 270 | return ${partial_success_return_val} |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 271 | } |
| 272 | |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 273 | # Erase an NVMe device. |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 274 | # |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 275 | # Use nvme sanitize if it is supported. Otherwise try nvme format with crypto |
| 276 | # mode, if that fails, then try to do with user data mode with a timeout |
| 277 | # proportional to the size of the device. |
| 278 | # Arguments: |
| 279 | # - character device to erase |
| 280 | # - strict: if set to "1", then only returns success if sanitize successfully |
| 281 | # physically erases the device. If left empty or set to "0", returns |
| 282 | # success if either sanitize or nvme format succeeds. |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 283 | secure_erase_nvme() { |
| 284 | local disk="$1" |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 285 | local strict="$2" |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 286 | local ses_user="1" # 0: no secure, 1: user data erase, 2: cryptographic erase |
| 287 | local ses_crypto="2" |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 288 | local base_timeout_in_ms=$(( 60 * 1000 )) # base timeout for 1 minute |
| 289 | local dev_size="$(get_nvme_char_device_size "${disk}")" |
| 290 | local bytes_per_ms=$(( 100 * 1024 )) # 100 MB/s |
| 291 | # Maximum time without any progress during sanitize operation |
| 292 | local sanitize_timeout_in_sec=$(( 20 * 60 )) # sanitize timeout for 20 minutes |
| 293 | # Maximum time to finish format operation. Let timeout be proportional to the |
| 294 | # size of device. |
| 295 | local format_timeout_in_ms=$(( base_timeout_in_ms + dev_size / bytes_per_ms )) |
| 296 | local progress current_progress |
| 297 | # "During a sanitize operation, the host may periodically examine the |
| 298 | # Sanitize Status log page to check for progress, however, the host |
| 299 | # should limit this polling (e.g., to at most once every several minutes) |
| 300 | # to avoid interfering with the progress of the sanitize operation itself." |
| 301 | # See section 8.15 in |
| 302 | # https://www.nvmexpress.org/wp-content/uploads/NVM_Express_Revision_1.3.pdf |
| 303 | # Because of the reason above need to sleep and check the progress during |
| 304 | # sanitize operation. We use 10 seconds for sleeping because the device we |
| 305 | # tested this code on executes the command quite fast. The whole full disk |
| 306 | # wipe process takes a lot of time so we don't want to increase it even more |
| 307 | # by picking a larger value. |
| 308 | local sleep_time_in_sec=10 |
| 309 | |
| 310 | # Sanitize is preferred over format because it guarantees physical data |
| 311 | # destruction. |
| 312 | if is_nvme_sanitize_supported "${disk}"; then |
| 313 | nvme sanitize "${disk}" --ause --sanact=0x02 |
| 314 | count="${sanitize_timeout_in_sec}" |
| 315 | while [ ${count} -gt 0 ] && is_nvme_sanitize_in_progress "${disk}"; do |
| 316 | # See the comment above. |
| 317 | sleep "${sleep_time_in_sec}" |
| 318 | current_progress="$(get_nvme_sanitize_progress "${disk}")" |
| 319 | if [ "${progress}" = "${current_progress}" ]; then |
| 320 | : $(( count -= sleep_time_in_sec )) |
| 321 | else |
| 322 | count="${sanitize_timeout_in_sec}" |
| 323 | fi |
| 324 | progress="${current_progress}" |
| 325 | done |
| 326 | is_nvme_sanitize_successful "${disk}" && return 0 |
| 327 | |
| 328 | # If block erase sanitize operation fails, issue sanitize command with the |
| 329 | # 'Exit Failure Mode' action to recover from failure. |
| 330 | nvme sanitize "${disk}" --sanact=0x01 |
| 331 | count="${sanitize_timeout_in_sec}" |
| 332 | progress=0 |
| 333 | while [ ${count} -gt 0 ] && is_nvme_sanitize_in_progress "${disk}"; do |
| 334 | # Check if the sanitize command executed before continuing. |
| 335 | # Also see the comment above. |
| 336 | sleep "${sleep_time_in_sec}" |
| 337 | current_progress="$(get_nvme_sanitize_progress "${disk}")" |
| 338 | if [ "${progress}" = "${current_progress}" ]; then |
| 339 | : $(( count -= sleep_time_in_sec )) |
| 340 | else |
| 341 | count="${sanitize_timeout_in_sec}" |
| 342 | fi |
| 343 | progress="${current_progress}" |
| 344 | done |
| 345 | fi |
| 346 | |
| 347 | # If strict is set to 1, only sanitize should return success. |
| 348 | # We want to be able to distinguish between the successes in strict mode |
| 349 | # in order to inform users if their device is physically erased or not. |
| 350 | local success_return_value=0 |
| 351 | if [ "${strict}" = "1" ]; then |
| 352 | success_return_value=${STRICT_RETURN_VALUE} |
| 353 | fi |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 354 | |
| 355 | # Format with crypto mode |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 356 | nvme format "${disk}" --ses "${ses_crypto}" && return ${success_return_value} |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 357 | |
| 358 | # Format with userdata mode |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 359 | # 0xffffffff means format all namespaces of the given character device. |
| 360 | nvme format "${disk}" --namespace-id=0xffffffff --ses "${ses_user}" \ |
| 361 | --timeout "${format_timeout_in_ms}" |
| 362 | local return_val=$? |
| 363 | test ${return_val} -eq 0 && return ${success_return_value} |
| 364 | return ${return_val} |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 365 | } |
| 366 | |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 367 | # Erase a device using its internal firmware function. |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 368 | # |
| 369 | # Arguments: |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 370 | # - device to erase ("/dev/sda", "/dev/mmcblk0", "/dev/nvme0"). |
| 371 | # - strict: if set to 1 then only returns success if the physical data |
| 372 | # destruction is guaranteed by the command set that is used. If unset |
| 373 | # or set to "0", returns success if either physical data destruction |
| 374 | # is guaranteed or not. |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 375 | # Returns: |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 376 | # 0 if the erase is either not supported or completed. |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 377 | # !0 if the erase process could not complete or failed. |
| 378 | secure_erase() { |
| 379 | local disk="$1" |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 380 | local strict="$2" |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 381 | local disk_type=$(get_device_type "${disk}") |
Thiemo Nagel | eb49427 | 2019-09-04 12:20:08 +0200 | [diff] [blame] | 382 | # Identify if MMC or SATA. |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 383 | case "$disk_type" in |
| 384 | MMC) |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 385 | secure_erase_mmc "${disk}" "${strict}" |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 386 | ;; |
| 387 | ATA) |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 388 | secure_erase_sata "${disk}" "${strict}" |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 389 | ;; |
| 390 | NVME) |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 391 | secure_erase_nvme "${disk}" "${strict}" |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 392 | ;; |
| 393 | *) |
| 394 | echo "Unable to identify the type of disk: -${disk_type}-" |
| 395 | return 1 |
| 396 | esac |
| 397 | } |
| 398 | |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 399 | # Use fio to write/verify a pattern. |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 400 | # |
| 401 | # The first and last 1M of the disk are zeroed, the rest is written |
| 402 | # with a random patter fio can verify latter. |
| 403 | # |
| 404 | # Argument |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 405 | # - disk: the device to erase |
| 406 | # - disk_size: the size of the device |
| 407 | # - disk_op: "write" to write over the SSD, "verify" to check the SSD |
| 408 | # has been overwritten properly, "verify_disk_wipe" to check |
| 409 | # the SSD has been zeroed. |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 410 | # Returns: |
| 411 | # fio error code if fio could run, 1 otherwise. |
| 412 | perform_fio_op() { |
| 413 | # Globals, used by factory_secure.fio |
| 414 | local disk="$1" |
| 415 | local disk_size="$2" |
| 416 | local disk_op="$3" |
| 417 | |
| 418 | local dev_main_area_end=$(( ${disk_size} - 1048576 )) |
| 419 | local block_size=1048576 |
| 420 | local fio_err=0 |
| 421 | local fio_output="${TEST_FIO_OUTPUT}" |
| 422 | local fio_regex='/^(secure|fio)/s/.* err= *([[:digit:]]+).*/\1/p' |
| 423 | local input |
| 424 | |
| 425 | export FIO_DEV="${disk}" |
| 426 | export FIO_DEV_MAIN_AREA_SIZE=$(( ${disk_size} - 2097152 )) |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 427 | export OFFSET="1m" |
| 428 | export VERIFY="md5" |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 429 | |
| 430 | if [ -z "${fio_output}" ]; then |
aysusayin | 5278d6f | 2019-08-21 15:33:48 +0200 | [diff] [blame] | 431 | fio_output="$(mktemp -t fio_output_XXXXXX)" |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 432 | fi |
| 433 | case "$disk_op" in |
| 434 | write) |
| 435 | export FIO_VERIFY_ONLY=0 |
| 436 | # Erase the begining an the end of the drive. Write random first |
| 437 | # to ensure the data is scrambled. |
| 438 | for input in "urandom" "zero"; do |
| 439 | dd bs="${block_size}" of="${disk}" oflag=dsync iflag=fullblock \ |
| 440 | if=/dev/${input} count=1 |
| 441 | dd bs="${block_size}" of="${disk}" oflag=dsync iflag=fullblock \ |
| 442 | if=/dev/${input} seek=$(( dev_main_area_end / ${block_size} )) |
| 443 | done |
| 444 | ;; |
| 445 | verify) |
| 446 | export FIO_VERIFY_ONLY=1 |
| 447 | ;; |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 448 | verify_disk_wipe) |
| 449 | export FIO_VERIFY_ONLY=1 |
| 450 | export VERIFY="pattern" |
| 451 | export FIO_DEV_MAIN_AREA_SIZE=${disk_size} |
| 452 | export OFFSET="0" |
| 453 | ;; |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 454 | *) |
| 455 | echo "Unsupported operation: -${disk_op}-" |
| 456 | return 1 |
| 457 | esac |
| 458 | |
aysusayin | 5278d6f | 2019-08-21 15:33:48 +0200 | [diff] [blame] | 459 | local fio_script="$(mktemp -t fio_config_XXXXXX)" |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 460 | cat >"${fio_script}" <<HERE |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 461 | [secure] |
| 462 | filename=\${FIO_DEV} |
| 463 | ioengine=libaio |
| 464 | iodepth=32 |
| 465 | direct=1 |
| 466 | readwrite=write |
| 467 | bs=256k |
| 468 | |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 469 | offset=\${OFFSET} |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 470 | size=\${FIO_DEV_MAIN_AREA_SIZE} |
| 471 | do_verify=1 |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 472 | verify=\${VERIFY} |
| 473 | verify_pattern=0x0 |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 474 | verify_only=\${FIO_VERIFY_ONLY} |
| 475 | HERE |
| 476 | # Write a pattern on the media for future verification. |
aysusayin | e3eb987 | 2019-08-07 12:31:49 +0200 | [diff] [blame] | 477 | # fio configuration file use DEV, DEV_MAIN_AREA_SIZE and VERIFY_ONLY. |
| 478 | fio "${fio_script}" --output "${fio_output}" --aux-path="${TMPDIR:-/tmp}" |
Thiemo Nagel | deb8473 | 2019-07-17 15:14:34 +0200 | [diff] [blame] | 479 | rm -f "${fio_script}" |
| 480 | fio_err=$(sed -nr "${fio_regex}" "${fio_output}") |
| 481 | if [ -z "${fio_err}" ]; then |
| 482 | echo "-- output of fio not understood --" |
| 483 | cat "${fio_output}" |
| 484 | fio_err=1 |
| 485 | elif [ ${fio_err} -ne 0 ]; then |
| 486 | cat "${fio_output}" |
| 487 | if [ $FIO_VERIFY_ONLY -eq 0 ]; then |
| 488 | echo "-- writing pattern failed --" |
| 489 | echo "The storage device is not working properly." |
| 490 | else |
| 491 | # Check if we fail to read the device, or the pattern is wrong. |
| 492 | echo "-- verifying pattern failed --" |
| 493 | if [ ${fio_err} -eq 84 ]; then |
| 494 | echo -n "The storage device has either been tampered with or " |
| 495 | echo "not securely erased properly." |
| 496 | else |
| 497 | echo "Storage device broken: unable to read some sector from it." |
| 498 | fi |
| 499 | fi |
| 500 | else |
| 501 | rm "${fio_output}" |
| 502 | fi |
| 503 | return ${fio_err} |
| 504 | } |