Love Kumar | eb8eb90 | 2024-07-31 14:54:12 +0530 | [diff] [blame] | 1 | # SPDX-License-Identifier: GPL-2.0 |
| 2 | # (C) Copyright 2024, Advanced Micro Devices, Inc. |
| 3 | |
| 4 | """ |
| 5 | Note: This test relies on boardenv_* containing configuration values to define |
| 6 | spi minimum and maximum frequencies at which the flash part can operate on and |
| 7 | these tests run at different spi frequency randomised values in the range |
| 8 | multiple times based on the user defined iteration value. |
| 9 | It also defines the SPI bus number containing the SPI-flash chip, SPI |
| 10 | chip-select, SPI mode, SPI flash part name and timeout parameters. If minimum |
| 11 | and maximum frequency is not defined, it will run on freq 0 by default. |
| 12 | |
| 13 | Without the boardenv_* configuration, this test will be automatically skipped. |
| 14 | |
| 15 | It also relies on configuration values for supported flashes for lock and |
| 16 | unlock cases for SPI family flash. It will run lock-unlock cases only for the |
| 17 | supported flash parts. |
| 18 | |
| 19 | For Example: |
| 20 | |
| 21 | # Details of SPI device test parameters required for SPI device testing: |
| 22 | |
| 23 | # bus - SPI bus number to init the flash device |
| 24 | # chip_select - SPI chip select number to init the flash device |
| 25 | # min_freq - Minimum frequency in hz at which the flash part can operate, set 0 |
| 26 | # or None for default frequency |
| 27 | # max_freq - Maximum frequency in hz at which the flash part can operate, set 0 |
| 28 | # or None for default frequency |
| 29 | # mode - SPI mode to init the flash device |
| 30 | # part_name - SPI flash part name to be detected |
| 31 | # timeout - Default timeout to run the sf commands |
| 32 | # iteration - No of iteration to run SPI flash test |
| 33 | |
| 34 | env__spi_device_test = { |
| 35 | 'bus': 0, |
| 36 | 'chip_select': 0, |
| 37 | 'min_freq': 10000000, |
| 38 | 'max_freq': 100000000, |
| 39 | 'mode': 0, |
| 40 | 'part_name': 'n25q00a', |
| 41 | 'timeout': 100000, |
| 42 | 'iteration': 5, |
| 43 | } |
| 44 | |
| 45 | # supported_flash - Flash parts name which support lock-unlock functionality |
| 46 | env__spi_lock_unlock = { |
| 47 | 'supported_flash': 'mt25qu512a, n25q00a, n25q512ax3', |
| 48 | } |
| 49 | """ |
| 50 | |
| 51 | import random |
| 52 | import re |
| 53 | import pytest |
| 54 | import u_boot_utils |
| 55 | |
| 56 | SPI_DATA = {} |
| 57 | EXPECTED_ERASE = 'Erased: OK' |
| 58 | EXPECTED_WRITE = 'Written: OK' |
| 59 | EXPECTED_READ = 'Read: OK' |
| 60 | EXPECTED_ERASE_ERRORS = [ |
| 61 | 'Erase operation failed', |
| 62 | 'Attempted to modify a protected sector', |
| 63 | 'Erased: ERROR', |
| 64 | 'is protected and cannot be erased', |
| 65 | 'ERROR: flash area is locked', |
| 66 | ] |
| 67 | EXPECTED_WRITE_ERRORS = [ |
| 68 | 'ERROR: flash area is locked', |
| 69 | 'Program operation failed', |
| 70 | 'Attempted to modify a protected sector', |
| 71 | 'Written: ERROR', |
| 72 | ] |
| 73 | |
| 74 | def get_params_spi(u_boot_console): |
| 75 | ''' Get SPI device test parameters from boardenv file ''' |
| 76 | f = u_boot_console.config.env.get('env__spi_device_test', None) |
| 77 | if not f: |
Love Kumar | 625bc3f | 2024-11-15 18:38:00 +0530 | [diff] [blame^] | 78 | pytest.skip('No SPI test device configured') |
Love Kumar | eb8eb90 | 2024-07-31 14:54:12 +0530 | [diff] [blame] | 79 | |
| 80 | bus = f.get('bus', 0) |
| 81 | cs = f.get('chip_select', 0) |
| 82 | mode = f.get('mode', 0) |
| 83 | part_name = f.get('part_name', None) |
| 84 | timeout = f.get('timeout', None) |
| 85 | |
| 86 | if not part_name: |
Love Kumar | 625bc3f | 2024-11-15 18:38:00 +0530 | [diff] [blame^] | 87 | pytest.skip('No SPI test device configured') |
Love Kumar | eb8eb90 | 2024-07-31 14:54:12 +0530 | [diff] [blame] | 88 | |
| 89 | return bus, cs, mode, part_name, timeout |
| 90 | |
| 91 | def spi_find_freq_range(u_boot_console): |
| 92 | '''Find out minimum and maximum frequnecies that SPI device can operate''' |
| 93 | f = u_boot_console.config.env.get('env__spi_device_test', None) |
| 94 | if not f: |
Love Kumar | 625bc3f | 2024-11-15 18:38:00 +0530 | [diff] [blame^] | 95 | pytest.skip('No SPI test device configured') |
Love Kumar | eb8eb90 | 2024-07-31 14:54:12 +0530 | [diff] [blame] | 96 | |
| 97 | min_f = f.get('min_freq', None) |
| 98 | max_f = f.get('max_freq', None) |
| 99 | iterations = f.get('iteration', 1) |
| 100 | |
| 101 | if not min_f: |
| 102 | min_f = 0 |
| 103 | if not max_f: |
| 104 | max_f = 0 |
| 105 | |
| 106 | max_f = max(max_f, min_f) |
| 107 | |
| 108 | return min_f, max_f, iterations |
| 109 | |
| 110 | def spi_pre_commands(u_boot_console, freq): |
| 111 | ''' Find out SPI family flash memory parameters ''' |
| 112 | bus, cs, mode, part_name, timeout = get_params_spi(u_boot_console) |
| 113 | |
| 114 | output = u_boot_console.run_command(f'sf probe {bus}:{cs} {freq} {mode}') |
| 115 | if not 'SF: Detected' in output: |
| 116 | pytest.fail('No SPI device available') |
| 117 | |
| 118 | if not part_name in output: |
Love Kumar | 625bc3f | 2024-11-15 18:38:00 +0530 | [diff] [blame^] | 119 | pytest.fail('Not recognized the SPI flash part name') |
Love Kumar | eb8eb90 | 2024-07-31 14:54:12 +0530 | [diff] [blame] | 120 | |
| 121 | m = re.search('page size (.+?) Bytes', output) |
| 122 | if m: |
| 123 | try: |
| 124 | page_size = int(m.group(1)) |
| 125 | except ValueError: |
Love Kumar | 625bc3f | 2024-11-15 18:38:00 +0530 | [diff] [blame^] | 126 | pytest.fail('Not recognized the SPI page size') |
Love Kumar | eb8eb90 | 2024-07-31 14:54:12 +0530 | [diff] [blame] | 127 | |
| 128 | m = re.search('erase size (.+?) KiB', output) |
| 129 | if m: |
| 130 | try: |
| 131 | erase_size = int(m.group(1)) |
| 132 | except ValueError: |
Love Kumar | 625bc3f | 2024-11-15 18:38:00 +0530 | [diff] [blame^] | 133 | pytest.fail('Not recognized the SPI erase size') |
Love Kumar | eb8eb90 | 2024-07-31 14:54:12 +0530 | [diff] [blame] | 134 | |
| 135 | erase_size *= 1024 |
| 136 | |
| 137 | m = re.search('total (.+?) MiB', output) |
| 138 | if m: |
| 139 | try: |
| 140 | total_size = int(m.group(1)) |
| 141 | except ValueError: |
Love Kumar | 625bc3f | 2024-11-15 18:38:00 +0530 | [diff] [blame^] | 142 | pytest.fail('Not recognized the SPI total size') |
Love Kumar | eb8eb90 | 2024-07-31 14:54:12 +0530 | [diff] [blame] | 143 | |
| 144 | total_size *= 1024 * 1024 |
| 145 | |
| 146 | m = re.search('Detected (.+?) with', output) |
| 147 | if m: |
| 148 | try: |
| 149 | flash_part = m.group(1) |
| 150 | assert flash_part == part_name |
| 151 | except ValueError: |
Love Kumar | 625bc3f | 2024-11-15 18:38:00 +0530 | [diff] [blame^] | 152 | pytest.fail('Not recognized the SPI flash part') |
Love Kumar | eb8eb90 | 2024-07-31 14:54:12 +0530 | [diff] [blame] | 153 | |
| 154 | global SPI_DATA |
| 155 | SPI_DATA = { |
| 156 | 'page_size': page_size, |
| 157 | 'erase_size': erase_size, |
| 158 | 'total_size': total_size, |
| 159 | 'flash_part': flash_part, |
| 160 | 'timeout': timeout, |
| 161 | } |
| 162 | |
| 163 | def get_page_size(): |
| 164 | ''' Get the SPI page size from spi data ''' |
| 165 | return SPI_DATA['page_size'] |
| 166 | |
| 167 | def get_erase_size(): |
| 168 | ''' Get the SPI erase size from spi data ''' |
| 169 | return SPI_DATA['erase_size'] |
| 170 | |
| 171 | def get_total_size(): |
| 172 | ''' Get the SPI total size from spi data ''' |
| 173 | return SPI_DATA['total_size'] |
| 174 | |
| 175 | def get_flash_part(): |
| 176 | ''' Get the SPI flash part name from spi data ''' |
| 177 | return SPI_DATA['flash_part'] |
| 178 | |
| 179 | def get_timeout(): |
| 180 | ''' Get the SPI timeout from spi data ''' |
| 181 | return SPI_DATA['timeout'] |
| 182 | |
| 183 | def spi_erase_block(u_boot_console, erase_size, total_size): |
| 184 | ''' Erase SPI flash memory block wise ''' |
| 185 | for start in range(0, total_size, erase_size): |
| 186 | output = u_boot_console.run_command(f'sf erase {hex(start)} {hex(erase_size)}') |
| 187 | assert EXPECTED_ERASE in output |
| 188 | |
| 189 | @pytest.mark.buildconfigspec('cmd_sf') |
| 190 | def test_spi_erase_block(u_boot_console): |
| 191 | ''' Test case to check SPI erase functionality by erasing memory regions |
| 192 | block-wise ''' |
| 193 | |
| 194 | min_f, max_f, loop = spi_find_freq_range(u_boot_console) |
| 195 | i = 0 |
| 196 | while i < loop: |
| 197 | spi_pre_commands(u_boot_console, random.randint(min_f, max_f)) |
| 198 | spi_erase_block(u_boot_console, get_erase_size(), get_total_size()) |
| 199 | i = i + 1 |
| 200 | |
| 201 | def spi_write_twice(u_boot_console, page_size, erase_size, total_size, timeout): |
| 202 | ''' Random write till page size, random till size and full size ''' |
| 203 | addr = u_boot_utils.find_ram_base(u_boot_console) |
| 204 | |
| 205 | old_size = 0 |
| 206 | for size in ( |
| 207 | random.randint(4, page_size), |
| 208 | random.randint(page_size, total_size), |
| 209 | total_size, |
| 210 | ): |
| 211 | offset = random.randint(4, page_size) |
| 212 | offset = offset & ~3 |
| 213 | size = size & ~3 |
| 214 | size = size - old_size |
| 215 | output = u_boot_console.run_command(f'crc32 {hex(addr + total_size)} {hex(size)}') |
| 216 | m = re.search('==> (.+?)$', output) |
| 217 | if not m: |
| 218 | pytest.fail('CRC32 failed') |
| 219 | |
| 220 | expected_crc32 = m.group(1) |
| 221 | if old_size % page_size: |
| 222 | old_size = int(old_size / page_size) |
| 223 | old_size *= page_size |
| 224 | |
| 225 | if size % erase_size: |
| 226 | erasesize = int(size / erase_size + 1) |
| 227 | erasesize *= erase_size |
| 228 | |
| 229 | eraseoffset = int(old_size / erase_size) |
| 230 | eraseoffset *= erase_size |
| 231 | |
| 232 | timeout = 100000000 |
| 233 | with u_boot_console.temporary_timeout(timeout): |
| 234 | output = u_boot_console.run_command( |
| 235 | f'sf erase {hex(eraseoffset)} {hex(erasesize)}' |
| 236 | ) |
| 237 | assert EXPECTED_ERASE in output |
| 238 | |
| 239 | with u_boot_console.temporary_timeout(timeout): |
| 240 | output = u_boot_console.run_command( |
| 241 | f'sf write {hex(addr + total_size)} {hex(old_size)} {hex(size)}' |
| 242 | ) |
| 243 | assert EXPECTED_WRITE in output |
| 244 | with u_boot_console.temporary_timeout(timeout): |
| 245 | output = u_boot_console.run_command( |
| 246 | f'sf read {hex(addr + total_size + offset)} {hex(old_size)} {hex(size)}' |
| 247 | ) |
| 248 | assert EXPECTED_READ in output |
| 249 | output = u_boot_console.run_command( |
| 250 | f'crc32 {hex(addr + total_size + offset)} {hex(size)}' |
| 251 | ) |
| 252 | assert expected_crc32 in output |
| 253 | old_size = size |
| 254 | |
| 255 | @pytest.mark.buildconfigspec('cmd_bdi') |
| 256 | @pytest.mark.buildconfigspec('cmd_sf') |
| 257 | @pytest.mark.buildconfigspec('cmd_memory') |
| 258 | def test_spi_write_twice(u_boot_console): |
| 259 | ''' Test to write data with random size twice for SPI ''' |
| 260 | min_f, max_f, loop = spi_find_freq_range(u_boot_console) |
| 261 | i = 0 |
| 262 | while i < loop: |
| 263 | spi_pre_commands(u_boot_console, random.randint(min_f, max_f)) |
| 264 | spi_write_twice( |
| 265 | u_boot_console, |
| 266 | get_page_size(), |
| 267 | get_erase_size(), |
| 268 | get_total_size(), |
| 269 | get_timeout() |
| 270 | ) |
| 271 | i = i + 1 |
| 272 | |
| 273 | def spi_write_continues(u_boot_console, page_size, erase_size, total_size, timeout): |
| 274 | ''' Write with random size of data to continue SPI write case ''' |
| 275 | spi_erase_block(u_boot_console, erase_size, total_size) |
| 276 | addr = u_boot_utils.find_ram_base(u_boot_console) |
| 277 | |
| 278 | output = u_boot_console.run_command(f'crc32 {hex(addr + 0x10000)} {hex(total_size)}') |
| 279 | m = re.search('==> (.+?)$', output) |
| 280 | if not m: |
| 281 | pytest.fail('CRC32 failed') |
| 282 | expected_crc32 = m.group(1) |
| 283 | |
| 284 | old_size = 0 |
| 285 | for size in ( |
| 286 | random.randint(4, page_size), |
| 287 | random.randint(page_size, total_size), |
| 288 | total_size, |
| 289 | ): |
| 290 | size = size & ~3 |
| 291 | size = size - old_size |
| 292 | with u_boot_console.temporary_timeout(timeout): |
| 293 | output = u_boot_console.run_command( |
| 294 | f'sf write {hex(addr + 0x10000 + old_size)} {hex(old_size)} {hex(size)}' |
| 295 | ) |
| 296 | assert EXPECTED_WRITE in output |
| 297 | old_size += size |
| 298 | |
| 299 | with u_boot_console.temporary_timeout(timeout): |
| 300 | output = u_boot_console.run_command( |
| 301 | f'sf read {hex(addr + 0x10000 + total_size)} 0 {hex(total_size)}' |
| 302 | ) |
| 303 | assert EXPECTED_READ in output |
| 304 | |
| 305 | output = u_boot_console.run_command( |
| 306 | f'crc32 {hex(addr + 0x10000 + total_size)} {hex(total_size)}' |
| 307 | ) |
| 308 | assert expected_crc32 in output |
| 309 | |
| 310 | @pytest.mark.buildconfigspec('cmd_bdi') |
| 311 | @pytest.mark.buildconfigspec('cmd_sf') |
| 312 | @pytest.mark.buildconfigspec('cmd_memory') |
| 313 | def test_spi_write_continues(u_boot_console): |
| 314 | ''' Test to write more random size data for SPI ''' |
| 315 | min_f, max_f, loop = spi_find_freq_range(u_boot_console) |
| 316 | i = 0 |
| 317 | while i < loop: |
| 318 | spi_pre_commands(u_boot_console, random.randint(min_f, max_f)) |
| 319 | spi_write_twice( |
| 320 | u_boot_console, |
| 321 | get_page_size(), |
| 322 | get_erase_size(), |
| 323 | get_total_size(), |
| 324 | get_timeout(), |
| 325 | ) |
| 326 | i = i + 1 |
| 327 | |
| 328 | def spi_read_twice(u_boot_console, page_size, total_size, timeout): |
| 329 | ''' Read the whole SPI flash twice, random_size till full flash size, |
| 330 | random till page size ''' |
| 331 | for size in random.randint(4, page_size), random.randint(4, total_size), total_size: |
| 332 | addr = u_boot_utils.find_ram_base(u_boot_console) |
| 333 | size = size & ~3 |
| 334 | with u_boot_console.temporary_timeout(timeout): |
| 335 | output = u_boot_console.run_command( |
| 336 | f'sf read {hex(addr + total_size)} 0 {hex(size)}' |
| 337 | ) |
| 338 | assert EXPECTED_READ in output |
| 339 | output = u_boot_console.run_command(f'crc32 {hex(addr + total_size)} {hex(size)}') |
| 340 | m = re.search('==> (.+?)$', output) |
| 341 | if not m: |
| 342 | pytest.fail('CRC32 failed') |
| 343 | expected_crc32 = m.group(1) |
| 344 | with u_boot_console.temporary_timeout(timeout): |
| 345 | output = u_boot_console.run_command( |
| 346 | f'sf read {hex(addr + total_size + 10)} 0 {hex(size)}' |
| 347 | ) |
| 348 | assert EXPECTED_READ in output |
| 349 | output = u_boot_console.run_command( |
| 350 | f'crc32 {hex(addr + total_size + 10)} {hex(size)}' |
| 351 | ) |
| 352 | assert expected_crc32 in output |
| 353 | |
| 354 | @pytest.mark.buildconfigspec('cmd_sf') |
| 355 | @pytest.mark.buildconfigspec('cmd_bdi') |
| 356 | @pytest.mark.buildconfigspec('cmd_memory') |
| 357 | def test_spi_read_twice(u_boot_console): |
| 358 | ''' Test to read random data twice from SPI ''' |
| 359 | min_f, max_f, loop = spi_find_freq_range(u_boot_console) |
| 360 | i = 0 |
| 361 | while i < loop: |
| 362 | spi_pre_commands(u_boot_console, random.randint(min_f, max_f)) |
| 363 | spi_read_twice(u_boot_console, get_page_size(), get_total_size(), get_timeout()) |
| 364 | i = i + 1 |
| 365 | |
| 366 | def spi_erase_all(u_boot_console, total_size, timeout): |
| 367 | ''' Erase the full chip SPI ''' |
| 368 | start = 0 |
| 369 | with u_boot_console.temporary_timeout(timeout): |
| 370 | output = u_boot_console.run_command(f'sf erase {start} {hex(total_size)}') |
| 371 | assert EXPECTED_ERASE in output |
| 372 | |
| 373 | @pytest.mark.buildconfigspec('cmd_sf') |
| 374 | def test_spi_erase_all(u_boot_console): |
| 375 | ''' Test to check full chip erase for SPI ''' |
| 376 | min_f, max_f, loop = spi_find_freq_range(u_boot_console) |
| 377 | i = 0 |
| 378 | while i < loop: |
| 379 | spi_pre_commands(u_boot_console, random.randint(min_f, max_f)) |
| 380 | spi_erase_all(u_boot_console, get_total_size(), get_timeout()) |
| 381 | i = i + 1 |
| 382 | |
| 383 | def flash_ops( |
| 384 | u_boot_console, ops, start, size, offset=0, exp_ret=0, exp_str='', not_exp_str='' |
| 385 | ): |
| 386 | ''' Flash operations: erase, write and read ''' |
| 387 | |
| 388 | f = u_boot_console.config.env.get('env__spi_device_test', None) |
| 389 | if not f: |
| 390 | timeout = 1000000 |
| 391 | |
| 392 | timeout = f.get('timeout', 1000000) |
| 393 | |
| 394 | if ops == 'erase': |
| 395 | with u_boot_console.temporary_timeout(timeout): |
| 396 | output = u_boot_console.run_command(f'sf erase {hex(start)} {hex(size)}') |
| 397 | else: |
| 398 | with u_boot_console.temporary_timeout(timeout): |
| 399 | output = u_boot_console.run_command( |
| 400 | f'sf {ops} {hex(offset)} {hex(start)} {hex(size)}' |
| 401 | ) |
| 402 | |
| 403 | if exp_str: |
| 404 | assert exp_str in output |
| 405 | if not_exp_str: |
| 406 | assert not_exp_str not in output |
| 407 | |
| 408 | ret_code = u_boot_console.run_command('echo $?') |
| 409 | if exp_ret >= 0: |
| 410 | assert ret_code.endswith(str(exp_ret)) |
| 411 | |
| 412 | return output, ret_code |
| 413 | |
| 414 | def spi_unlock_exit(u_boot_console, addr, size): |
| 415 | ''' Unlock the flash before making it fail ''' |
| 416 | u_boot_console.run_command(f'sf protect unlock {hex(addr)} {hex(size)}') |
| 417 | assert False, 'FAIL: Flash lock is unable to protect the data!' |
| 418 | |
| 419 | def find_prot_region(lock_addr, lock_size): |
| 420 | ''' Get the protected and un-protected region of flash ''' |
| 421 | total_size = get_total_size() |
| 422 | erase_size = get_erase_size() |
| 423 | |
| 424 | if lock_addr < (total_size // 2): |
| 425 | sect_num = (lock_addr + lock_size) // erase_size |
| 426 | x = 1 |
| 427 | while x < sect_num: |
| 428 | x *= 2 |
| 429 | prot_start = 0 |
| 430 | prot_size = x * erase_size |
| 431 | unprot_start = prot_start + prot_size |
| 432 | unprot_size = total_size - unprot_start |
| 433 | else: |
| 434 | sect_num = (total_size - lock_addr) // erase_size |
| 435 | x = 1 |
| 436 | while x < sect_num: |
| 437 | x *= 2 |
| 438 | prot_start = total_size - (x * erase_size) |
| 439 | prot_size = total_size - prot_start |
| 440 | unprot_start = 0 |
| 441 | unprot_size = prot_start |
| 442 | |
| 443 | return prot_start, prot_size, unprot_start, unprot_size |
| 444 | |
| 445 | def protect_ops(u_boot_console, lock_addr, lock_size, ops="unlock"): |
| 446 | ''' Run the command to lock or Unlock the flash ''' |
| 447 | u_boot_console.run_command(f'sf protect {ops} {hex(lock_addr)} {hex(lock_size)}') |
| 448 | output = u_boot_console.run_command('echo $?') |
| 449 | if ops == "lock" and not output.endswith('0'): |
| 450 | u_boot_console.run_command(f'sf protect unlock {hex(lock_addr)} {hex(lock_size)}') |
| 451 | assert False, "sf protect lock command exits with non-zero return code" |
| 452 | assert output.endswith('0') |
| 453 | |
| 454 | def erase_write_ops(u_boot_console, start, size): |
| 455 | ''' Basic erase and write operation for flash ''' |
| 456 | addr = u_boot_utils.find_ram_base(u_boot_console) |
| 457 | flash_ops(u_boot_console, 'erase', start, size, 0, 0, EXPECTED_ERASE) |
| 458 | flash_ops(u_boot_console, 'write', start, size, addr, 0, EXPECTED_WRITE) |
| 459 | |
| 460 | def spi_lock_unlock(u_boot_console, lock_addr, lock_size): |
| 461 | ''' Lock unlock operations for SPI family flash ''' |
| 462 | addr = u_boot_utils.find_ram_base(u_boot_console) |
| 463 | erase_size = get_erase_size() |
| 464 | |
| 465 | # Find the protected/un-protected region |
| 466 | prot_start, prot_size, unprot_start, unprot_size = find_prot_region(lock_addr, lock_size) |
| 467 | |
| 468 | # Check erase/write operation before locking |
| 469 | erase_write_ops(u_boot_console, prot_start, prot_size) |
| 470 | |
| 471 | # Locking the flash |
| 472 | protect_ops(u_boot_console, lock_addr, lock_size, 'lock') |
| 473 | |
| 474 | # Check erase/write operation after locking |
| 475 | output, ret_code = flash_ops(u_boot_console, 'erase', prot_start, prot_size, 0, -1) |
| 476 | if not any(error in output for error in EXPECTED_ERASE_ERRORS) or ret_code.endswith( |
| 477 | '0' |
| 478 | ): |
| 479 | spi_unlock_exit(u_boot_console, lock_addr, lock_size) |
| 480 | |
| 481 | output, ret_code = flash_ops( |
| 482 | u_boot_console, 'write', prot_start, prot_size, addr, -1 |
| 483 | ) |
| 484 | if not any(error in output for error in EXPECTED_WRITE_ERRORS) or ret_code.endswith( |
| 485 | '0' |
| 486 | ): |
| 487 | spi_unlock_exit(u_boot_console, lock_addr, lock_size) |
| 488 | |
| 489 | # Check locked sectors |
| 490 | sect_lock_start = random.randrange(prot_start, (prot_start + prot_size), erase_size) |
| 491 | if prot_size > erase_size: |
| 492 | sect_lock_size = random.randrange( |
| 493 | erase_size, (prot_start + prot_size - sect_lock_start), erase_size |
| 494 | ) |
| 495 | else: |
| 496 | sect_lock_size = erase_size |
| 497 | sect_write_size = random.randint(1, sect_lock_size) |
| 498 | |
| 499 | output, ret_code = flash_ops( |
| 500 | u_boot_console, 'erase', sect_lock_start, sect_lock_size, 0, -1 |
| 501 | ) |
| 502 | if not any(error in output for error in EXPECTED_ERASE_ERRORS) or ret_code.endswith( |
| 503 | '0' |
| 504 | ): |
| 505 | spi_unlock_exit(u_boot_console, lock_addr, lock_size) |
| 506 | |
| 507 | output, ret_code = flash_ops( |
| 508 | u_boot_console, 'write', sect_lock_start, sect_write_size, addr, -1 |
| 509 | ) |
| 510 | if not any(error in output for error in EXPECTED_WRITE_ERRORS) or ret_code.endswith( |
| 511 | '0' |
| 512 | ): |
| 513 | spi_unlock_exit(u_boot_console, lock_addr, lock_size) |
| 514 | |
| 515 | # Check unlocked sectors |
| 516 | if unprot_size != 0: |
| 517 | sect_unlock_start = random.randrange( |
| 518 | unprot_start, (unprot_start + unprot_size), erase_size |
| 519 | ) |
| 520 | if unprot_size > erase_size: |
| 521 | sect_unlock_size = random.randrange( |
| 522 | erase_size, (unprot_start + unprot_size - sect_unlock_start), erase_size |
| 523 | ) |
| 524 | else: |
| 525 | sect_unlock_size = erase_size |
| 526 | sect_write_size = random.randint(1, sect_unlock_size) |
| 527 | |
| 528 | output, ret_code = flash_ops( |
| 529 | u_boot_console, 'erase', sect_unlock_start, sect_unlock_size, 0, -1 |
| 530 | ) |
| 531 | if EXPECTED_ERASE not in output or ret_code.endswith('1'): |
| 532 | spi_unlock_exit(u_boot_console, lock_addr, lock_size) |
| 533 | |
| 534 | output, ret_code = flash_ops( |
| 535 | u_boot_console, 'write', sect_unlock_start, sect_write_size, addr, -1 |
| 536 | ) |
| 537 | if EXPECTED_WRITE not in output or ret_code.endswith('1'): |
| 538 | spi_unlock_exit(u_boot_console, lock_addr, lock_size) |
| 539 | |
| 540 | # Unlocking the flash |
| 541 | protect_ops(u_boot_console, lock_addr, lock_size, 'unlock') |
| 542 | |
| 543 | # Check erase/write operation after un-locking |
| 544 | erase_write_ops(u_boot_console, prot_start, prot_size) |
| 545 | |
| 546 | # Check previous locked sectors |
| 547 | sect_lock_start = random.randrange(prot_start, (prot_start + prot_size), erase_size) |
| 548 | if prot_size > erase_size: |
| 549 | sect_lock_size = random.randrange( |
| 550 | erase_size, (prot_start + prot_size - sect_lock_start), erase_size |
| 551 | ) |
| 552 | else: |
| 553 | sect_lock_size = erase_size |
| 554 | sect_write_size = random.randint(1, sect_lock_size) |
| 555 | |
| 556 | flash_ops( |
| 557 | u_boot_console, 'erase', sect_lock_start, sect_lock_size, 0, 0, EXPECTED_ERASE |
| 558 | ) |
| 559 | flash_ops( |
| 560 | u_boot_console, |
| 561 | 'write', |
| 562 | sect_lock_start, |
| 563 | sect_write_size, |
| 564 | addr, |
| 565 | 0, |
| 566 | EXPECTED_WRITE, |
| 567 | ) |
| 568 | |
| 569 | @pytest.mark.buildconfigspec('cmd_bdi') |
| 570 | @pytest.mark.buildconfigspec('cmd_sf') |
| 571 | @pytest.mark.buildconfigspec('cmd_memory') |
| 572 | def test_spi_lock_unlock(u_boot_console): |
| 573 | ''' Test to check the lock-unlock functionality for SPI family flash ''' |
| 574 | min_f, max_f, loop = spi_find_freq_range(u_boot_console) |
| 575 | flashes = u_boot_console.config.env.get('env__spi_lock_unlock', False) |
| 576 | if not flashes: |
Love Kumar | 625bc3f | 2024-11-15 18:38:00 +0530 | [diff] [blame^] | 577 | pytest.skip('No SPI test device configured for lock/unlock') |
Love Kumar | eb8eb90 | 2024-07-31 14:54:12 +0530 | [diff] [blame] | 578 | |
| 579 | i = 0 |
| 580 | while i < loop: |
| 581 | spi_pre_commands(u_boot_console, random.randint(min_f, max_f)) |
| 582 | total_size = get_total_size() |
| 583 | flash_part = get_flash_part() |
| 584 | |
| 585 | flashes_list = flashes.get('supported_flash', None).split(',') |
| 586 | flashes_list = [x.strip() for x in flashes_list] |
| 587 | if flash_part not in flashes_list: |
| 588 | pytest.skip('Detected flash does not support lock/unlock') |
| 589 | |
| 590 | # For lower half of memory |
| 591 | lock_addr = random.randint(0, (total_size // 2) - 1) |
| 592 | lock_size = random.randint(1, ((total_size // 2) - lock_addr)) |
| 593 | spi_lock_unlock(u_boot_console, lock_addr, lock_size) |
| 594 | |
| 595 | # For upper half of memory |
| 596 | lock_addr = random.randint((total_size // 2), total_size - 1) |
| 597 | lock_size = random.randint(1, (total_size - lock_addr)) |
| 598 | spi_lock_unlock(u_boot_console, lock_addr, lock_size) |
| 599 | |
| 600 | # For entire flash |
| 601 | lock_addr = random.randint(0, total_size - 1) |
| 602 | lock_size = random.randint(1, (total_size - lock_addr)) |
| 603 | spi_lock_unlock(u_boot_console, lock_addr, lock_size) |
| 604 | |
| 605 | i = i + 1 |
| 606 | |
| 607 | @pytest.mark.buildconfigspec('cmd_bdi') |
| 608 | @pytest.mark.buildconfigspec('cmd_sf') |
| 609 | @pytest.mark.buildconfigspec('cmd_memory') |
| 610 | def test_spi_negative(u_boot_console): |
| 611 | ''' Negative tests for SPI ''' |
| 612 | min_f, max_f, loop = spi_find_freq_range(u_boot_console) |
| 613 | spi_pre_commands(u_boot_console, random.randint(min_f, max_f)) |
| 614 | total_size = get_total_size() |
| 615 | erase_size = get_erase_size() |
| 616 | page_size = get_page_size() |
| 617 | addr = u_boot_utils.find_ram_base(u_boot_console) |
| 618 | i = 0 |
| 619 | while i < loop: |
| 620 | # Erase negative test |
| 621 | start = random.randint(0, total_size) |
| 622 | esize = erase_size |
| 623 | |
| 624 | # If erasesize is not multiple of flash's erase size |
| 625 | while esize % erase_size == 0: |
| 626 | esize = random.randint(0, total_size - start) |
| 627 | |
| 628 | error_msg = 'Erased: ERROR' |
| 629 | flash_ops( |
| 630 | u_boot_console, 'erase', start, esize, 0, 1, error_msg, EXPECTED_ERASE |
| 631 | ) |
| 632 | |
| 633 | # If eraseoffset exceeds beyond flash size |
| 634 | eoffset = random.randint(total_size, (total_size + int(0x1000000))) |
| 635 | error_msg = 'Offset exceeds device limit' |
| 636 | flash_ops( |
| 637 | u_boot_console, 'erase', eoffset, esize, 0, 1, error_msg, EXPECTED_ERASE |
| 638 | ) |
| 639 | |
| 640 | # If erasesize exceeds beyond flash size |
| 641 | esize = random.randint((total_size - start), (total_size + int(0x1000000))) |
| 642 | error_msg = 'ERROR: attempting erase past flash size' |
| 643 | flash_ops( |
| 644 | u_boot_console, 'erase', start, esize, 0, 1, error_msg, EXPECTED_ERASE |
| 645 | ) |
| 646 | |
| 647 | # If erase size is 0 |
| 648 | esize = 0 |
Love Kumar | 9339e51 | 2024-09-03 00:08:17 +0530 | [diff] [blame] | 649 | error_msg = None |
Love Kumar | eb8eb90 | 2024-07-31 14:54:12 +0530 | [diff] [blame] | 650 | flash_ops( |
| 651 | u_boot_console, 'erase', start, esize, 0, 1, error_msg, EXPECTED_ERASE |
| 652 | ) |
| 653 | |
| 654 | # If erasesize is less than flash's page size |
| 655 | esize = random.randint(0, page_size) |
| 656 | start = random.randint(0, (total_size - page_size)) |
| 657 | error_msg = 'Erased: ERROR' |
| 658 | flash_ops( |
| 659 | u_boot_console, 'erase', start, esize, 0, 1, error_msg, EXPECTED_ERASE |
| 660 | ) |
| 661 | |
| 662 | # Write/Read negative test |
| 663 | # if Write/Read size exceeds beyond flash size |
| 664 | offset = random.randint(0, total_size) |
| 665 | size = random.randint((total_size - offset), (total_size + int(0x1000000))) |
| 666 | error_msg = 'Size exceeds partition or device limit' |
| 667 | flash_ops( |
| 668 | u_boot_console, 'write', offset, size, addr, 1, error_msg, EXPECTED_WRITE |
| 669 | ) |
| 670 | flash_ops( |
| 671 | u_boot_console, 'read', offset, size, addr, 1, error_msg, EXPECTED_READ |
| 672 | ) |
| 673 | |
| 674 | # if Write/Read offset exceeds beyond flash size |
| 675 | offset = random.randint(total_size, (total_size + int(0x1000000))) |
| 676 | size = random.randint(0, total_size) |
| 677 | error_msg = 'Offset exceeds device limit' |
| 678 | flash_ops( |
| 679 | u_boot_console, 'write', offset, size, addr, 1, error_msg, EXPECTED_WRITE |
| 680 | ) |
| 681 | flash_ops( |
| 682 | u_boot_console, 'read', offset, size, addr, 1, error_msg, EXPECTED_READ |
| 683 | ) |
| 684 | |
| 685 | # if Write/Read size is 0 |
| 686 | offset = random.randint(0, 2) |
| 687 | size = 0 |
Love Kumar | 9339e51 | 2024-09-03 00:08:17 +0530 | [diff] [blame] | 688 | error_msg = None |
Love Kumar | eb8eb90 | 2024-07-31 14:54:12 +0530 | [diff] [blame] | 689 | flash_ops( |
| 690 | u_boot_console, 'write', offset, size, addr, 1, error_msg, EXPECTED_WRITE |
| 691 | ) |
| 692 | flash_ops( |
| 693 | u_boot_console, 'read', offset, size, addr, 1, error_msg, EXPECTED_READ |
| 694 | ) |
| 695 | |
Padmarao Begari | 276495d | 2024-10-29 17:17:09 +0530 | [diff] [blame] | 696 | # Read to relocation address |
| 697 | output = u_boot_console.run_command('bdinfo') |
Simon Glass | 8be1fe5 | 2024-11-12 07:13:25 -0700 | [diff] [blame] | 698 | m = re.search(r'relocaddr\s*= (.+)', output) |
Padmarao Begari | 276495d | 2024-10-29 17:17:09 +0530 | [diff] [blame] | 699 | res_area = int(m.group(1), 16) |
| 700 | |
| 701 | start = 0 |
| 702 | size = 0x2000 |
| 703 | error_msg = 'ERROR: trying to overwrite reserved memory' |
| 704 | flash_ops( |
| 705 | u_boot_console, 'read', start, size, res_area, 1, error_msg, EXPECTED_READ |
| 706 | ) |
| 707 | |
Love Kumar | eb8eb90 | 2024-07-31 14:54:12 +0530 | [diff] [blame] | 708 | i = i + 1 |