| /* |
| * Copyright (c) 2020 The Fuchsia Authors |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include "abr-wear-leveling.h" |
| |
| #if defined(ABR_WEAR_LEVELING_DEBUG) |
| #define abrwlP(fmt...) \ |
| printf("[abr-wear-leveling]%s:%d:", __func__, __LINE__), printf(fmt) |
| #else |
| #define abrwlP(fmt...) |
| #endif |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| void set_abr_metadata_ext_magic(struct abr_metadata_ext *data) |
| { |
| data->magic[0] = ABR_WEAR_LEVELING_MAGIC_BYTE_0; |
| data->magic[1] = ABR_WEAR_LEVELING_MAGIC_BYTE_1; |
| data->magic[2] = ABR_WEAR_LEVELING_MAGIC_BYTE_2; |
| data->magic[3] = ABR_WEAR_LEVELING_MAGIC_BYTE_3; |
| } |
| |
| bool abr_metadata_ext_valid(const void *abr_data) |
| { |
| const uint8_t *start = (const uint8_t *)abr_data; |
| return start[ABR_WEAR_LEVELING_MAGIC_OFFSET] == |
| ABR_WEAR_LEVELING_MAGIC_BYTE_0 && |
| start[ABR_WEAR_LEVELING_MAGIC_OFFSET + 1] == |
| ABR_WEAR_LEVELING_MAGIC_BYTE_1 && |
| start[ABR_WEAR_LEVELING_MAGIC_OFFSET + 2] == |
| ABR_WEAR_LEVELING_MAGIC_BYTE_2 && |
| start[ABR_WEAR_LEVELING_MAGIC_OFFSET + 3] == |
| ABR_WEAR_LEVELING_MAGIC_BYTE_3; |
| } |
| |
| bool layout_support_wear_leveling(const struct sysconfig_header *header, |
| size_t page_size) |
| { |
| // Abr metadata sub-partition needs to be at the end. |
| return header->abr_metadata.size > page_size && |
| header->abr_metadata.offset >= header->sysconfig_data.offset && |
| header->abr_metadata.offset >= header->vb_metadata_a.offset && |
| header->abr_metadata.offset >= header->vb_metadata_b.offset && |
| header->abr_metadata.offset >= header->vb_metadata_r.offset; |
| } |
| |
| int find_latest_abr_metadata_page(const struct sysconfig_header *header, |
| const void *abr_subpart, uint64_t page_size, |
| struct abr_metadata_ext *out) |
| { |
| // Abr metadatas are appended from the first to the last page in the |
| // sub-partition. Thus, we scan backward and find the first valid |
| // page. |
| int i, num_pages = header->abr_metadata.size / page_size; |
| const uint8_t *start = (const uint8_t *)abr_subpart; |
| abrwlP("Finding page with latest abr metadata\n"); |
| for (i = num_pages - 1; i >= 0; i--) { |
| if (abr_metadata_ext_valid(start + i * page_size)) { |
| abrwlP("page %d has valid abr metadata\n", i); |
| memcpy(out, start + i * page_size, |
| sizeof(struct abr_metadata_ext)); |
| return i; |
| } |
| } |
| // Default to the first page if there is no page with valid magic, |
| abrwlP("no page with valid magic found. use page 0 as default\n"); |
| memcpy(out, start, sizeof(struct abr_metadata_ext)); |
| return 0; |
| } |
| |
| bool find_empty_page_for_wear_leveling(const struct sysconfig_header *header, |
| const uint8_t *abr_subpart, |
| uint64_t page_size, int64_t *out) |
| { |
| // NAND page programming has to be consecutive from the first to last |
| // within a block. Thus we find the first empty page such that all pages |
| // behind it are also empty, or in other work, the immediate empty page |
| // after the last non-empty page in the sub-partition; |
| int64_t i, j, num_pages = header->abr_metadata.size / page_size; |
| |
| const uint8_t *page; |
| abrwlP("Finding empty page, total pages: %lld\n", num_pages); |
| for (i = 1; i <= num_pages; i++) { |
| page = abr_subpart + (num_pages - i) * page_size; |
| bool page_empty = true; |
| // Check whether the page is empty. |
| for (j = 0; j < (int64_t)page_size; j++) { |
| if (page[j] != 0xff) { |
| abrwlP("page %lld, offset %lld = 0x%x != 0xff\n", |
| (num_pages - i), j, page[j]); |
| page_empty = false; |
| break; |
| } |
| } |
| |
| if (!page_empty) { |
| abrwlP("page %lld non-empty\n", (num_pages - i)); |
| break; |
| } |
| |
| abrwlP("page %lld empty\n", (num_pages - i)); |
| } |
| *out = num_pages - i + 1; |
| abrwlP("using empty page %lld\n", *out); |
| // If no page is empty, *out will be equal to num_pages. |
| return *out < num_pages; |
| } |