162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * fs/partitions/aix.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012-2013 Philippe De Muyter <phdm@macqel.be> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include "check.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_cistruct lvm_rec { 1162306a36Sopenharmony_ci char lvm_id[4]; /* "_LVM" */ 1262306a36Sopenharmony_ci char reserved4[16]; 1362306a36Sopenharmony_ci __be32 lvmarea_len; 1462306a36Sopenharmony_ci __be32 vgda_len; 1562306a36Sopenharmony_ci __be32 vgda_psn[2]; 1662306a36Sopenharmony_ci char reserved36[10]; 1762306a36Sopenharmony_ci __be16 pp_size; /* log2(pp_size) */ 1862306a36Sopenharmony_ci char reserved46[12]; 1962306a36Sopenharmony_ci __be16 version; 2062306a36Sopenharmony_ci }; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistruct vgda { 2362306a36Sopenharmony_ci __be32 secs; 2462306a36Sopenharmony_ci __be32 usec; 2562306a36Sopenharmony_ci char reserved8[16]; 2662306a36Sopenharmony_ci __be16 numlvs; 2762306a36Sopenharmony_ci __be16 maxlvs; 2862306a36Sopenharmony_ci __be16 pp_size; 2962306a36Sopenharmony_ci __be16 numpvs; 3062306a36Sopenharmony_ci __be16 total_vgdas; 3162306a36Sopenharmony_ci __be16 vgda_size; 3262306a36Sopenharmony_ci }; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistruct lvd { 3562306a36Sopenharmony_ci __be16 lv_ix; 3662306a36Sopenharmony_ci __be16 res2; 3762306a36Sopenharmony_ci __be16 res4; 3862306a36Sopenharmony_ci __be16 maxsize; 3962306a36Sopenharmony_ci __be16 lv_state; 4062306a36Sopenharmony_ci __be16 mirror; 4162306a36Sopenharmony_ci __be16 mirror_policy; 4262306a36Sopenharmony_ci __be16 num_lps; 4362306a36Sopenharmony_ci __be16 res10[8]; 4462306a36Sopenharmony_ci }; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistruct lvname { 4762306a36Sopenharmony_ci char name[64]; 4862306a36Sopenharmony_ci }; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistruct ppe { 5162306a36Sopenharmony_ci __be16 lv_ix; 5262306a36Sopenharmony_ci unsigned short res2; 5362306a36Sopenharmony_ci unsigned short res4; 5462306a36Sopenharmony_ci __be16 lp_ix; 5562306a36Sopenharmony_ci unsigned short res8[12]; 5662306a36Sopenharmony_ci }; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistruct pvd { 5962306a36Sopenharmony_ci char reserved0[16]; 6062306a36Sopenharmony_ci __be16 pp_count; 6162306a36Sopenharmony_ci char reserved18[2]; 6262306a36Sopenharmony_ci __be32 psn_part1; 6362306a36Sopenharmony_ci char reserved24[8]; 6462306a36Sopenharmony_ci struct ppe ppe[1016]; 6562306a36Sopenharmony_ci }; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define LVM_MAXLVS 256 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/** 7062306a36Sopenharmony_ci * read_lba(): Read bytes from disk, starting at given LBA 7162306a36Sopenharmony_ci * @state 7262306a36Sopenharmony_ci * @lba 7362306a36Sopenharmony_ci * @buffer 7462306a36Sopenharmony_ci * @count 7562306a36Sopenharmony_ci * 7662306a36Sopenharmony_ci * Description: Reads @count bytes from @state->disk into @buffer. 7762306a36Sopenharmony_ci * Returns number of bytes read on success, 0 on error. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_cistatic size_t read_lba(struct parsed_partitions *state, u64 lba, u8 *buffer, 8062306a36Sopenharmony_ci size_t count) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci size_t totalreadcount = 0; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (!buffer || lba + count / 512 > get_capacity(state->disk) - 1ULL) 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci while (count) { 8862306a36Sopenharmony_ci int copied = 512; 8962306a36Sopenharmony_ci Sector sect; 9062306a36Sopenharmony_ci unsigned char *data = read_part_sector(state, lba++, §); 9162306a36Sopenharmony_ci if (!data) 9262306a36Sopenharmony_ci break; 9362306a36Sopenharmony_ci if (copied > count) 9462306a36Sopenharmony_ci copied = count; 9562306a36Sopenharmony_ci memcpy(buffer, data, copied); 9662306a36Sopenharmony_ci put_dev_sector(sect); 9762306a36Sopenharmony_ci buffer += copied; 9862306a36Sopenharmony_ci totalreadcount += copied; 9962306a36Sopenharmony_ci count -= copied; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci return totalreadcount; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/** 10562306a36Sopenharmony_ci * alloc_pvd(): reads physical volume descriptor 10662306a36Sopenharmony_ci * @state 10762306a36Sopenharmony_ci * @lba 10862306a36Sopenharmony_ci * 10962306a36Sopenharmony_ci * Description: Returns pvd on success, NULL on error. 11062306a36Sopenharmony_ci * Allocates space for pvd and fill it with disk blocks at @lba 11162306a36Sopenharmony_ci * Notes: remember to free pvd when you're done! 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_cistatic struct pvd *alloc_pvd(struct parsed_partitions *state, u32 lba) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci size_t count = sizeof(struct pvd); 11662306a36Sopenharmony_ci struct pvd *p; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci p = kmalloc(count, GFP_KERNEL); 11962306a36Sopenharmony_ci if (!p) 12062306a36Sopenharmony_ci return NULL; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (read_lba(state, lba, (u8 *) p, count) < count) { 12362306a36Sopenharmony_ci kfree(p); 12462306a36Sopenharmony_ci return NULL; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci return p; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/** 13062306a36Sopenharmony_ci * alloc_lvn(): reads logical volume names 13162306a36Sopenharmony_ci * @state 13262306a36Sopenharmony_ci * @lba 13362306a36Sopenharmony_ci * 13462306a36Sopenharmony_ci * Description: Returns lvn on success, NULL on error. 13562306a36Sopenharmony_ci * Allocates space for lvn and fill it with disk blocks at @lba 13662306a36Sopenharmony_ci * Notes: remember to free lvn when you're done! 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_cistatic struct lvname *alloc_lvn(struct parsed_partitions *state, u32 lba) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci size_t count = sizeof(struct lvname) * LVM_MAXLVS; 14162306a36Sopenharmony_ci struct lvname *p; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci p = kmalloc(count, GFP_KERNEL); 14462306a36Sopenharmony_ci if (!p) 14562306a36Sopenharmony_ci return NULL; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (read_lba(state, lba, (u8 *) p, count) < count) { 14862306a36Sopenharmony_ci kfree(p); 14962306a36Sopenharmony_ci return NULL; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci return p; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ciint aix_partition(struct parsed_partitions *state) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci int ret = 0; 15762306a36Sopenharmony_ci Sector sect; 15862306a36Sopenharmony_ci unsigned char *d; 15962306a36Sopenharmony_ci u32 pp_bytes_size; 16062306a36Sopenharmony_ci u32 pp_blocks_size = 0; 16162306a36Sopenharmony_ci u32 vgda_sector = 0; 16262306a36Sopenharmony_ci u32 vgda_len = 0; 16362306a36Sopenharmony_ci int numlvs = 0; 16462306a36Sopenharmony_ci struct pvd *pvd = NULL; 16562306a36Sopenharmony_ci struct lv_info { 16662306a36Sopenharmony_ci unsigned short pps_per_lv; 16762306a36Sopenharmony_ci unsigned short pps_found; 16862306a36Sopenharmony_ci unsigned char lv_is_contiguous; 16962306a36Sopenharmony_ci } *lvip; 17062306a36Sopenharmony_ci struct lvname *n = NULL; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci d = read_part_sector(state, 7, §); 17362306a36Sopenharmony_ci if (d) { 17462306a36Sopenharmony_ci struct lvm_rec *p = (struct lvm_rec *)d; 17562306a36Sopenharmony_ci u16 lvm_version = be16_to_cpu(p->version); 17662306a36Sopenharmony_ci char tmp[64]; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (lvm_version == 1) { 17962306a36Sopenharmony_ci int pp_size_log2 = be16_to_cpu(p->pp_size); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci pp_bytes_size = 1 << pp_size_log2; 18262306a36Sopenharmony_ci pp_blocks_size = pp_bytes_size / 512; 18362306a36Sopenharmony_ci snprintf(tmp, sizeof(tmp), 18462306a36Sopenharmony_ci " AIX LVM header version %u found\n", 18562306a36Sopenharmony_ci lvm_version); 18662306a36Sopenharmony_ci vgda_len = be32_to_cpu(p->vgda_len); 18762306a36Sopenharmony_ci vgda_sector = be32_to_cpu(p->vgda_psn[0]); 18862306a36Sopenharmony_ci } else { 18962306a36Sopenharmony_ci snprintf(tmp, sizeof(tmp), 19062306a36Sopenharmony_ci " unsupported AIX LVM version %d found\n", 19162306a36Sopenharmony_ci lvm_version); 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci strlcat(state->pp_buf, tmp, PAGE_SIZE); 19462306a36Sopenharmony_ci put_dev_sector(sect); 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci if (vgda_sector && (d = read_part_sector(state, vgda_sector, §))) { 19762306a36Sopenharmony_ci struct vgda *p = (struct vgda *)d; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci numlvs = be16_to_cpu(p->numlvs); 20062306a36Sopenharmony_ci put_dev_sector(sect); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci lvip = kcalloc(state->limit, sizeof(struct lv_info), GFP_KERNEL); 20362306a36Sopenharmony_ci if (!lvip) 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci if (numlvs && (d = read_part_sector(state, vgda_sector + 1, §))) { 20662306a36Sopenharmony_ci struct lvd *p = (struct lvd *)d; 20762306a36Sopenharmony_ci int i; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci n = alloc_lvn(state, vgda_sector + vgda_len - 33); 21062306a36Sopenharmony_ci if (n) { 21162306a36Sopenharmony_ci int foundlvs = 0; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci for (i = 0; foundlvs < numlvs && i < state->limit; i += 1) { 21462306a36Sopenharmony_ci lvip[i].pps_per_lv = be16_to_cpu(p[i].num_lps); 21562306a36Sopenharmony_ci if (lvip[i].pps_per_lv) 21662306a36Sopenharmony_ci foundlvs += 1; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci /* pvd loops depend on n[].name and lvip[].pps_per_lv */ 21962306a36Sopenharmony_ci pvd = alloc_pvd(state, vgda_sector + 17); 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci put_dev_sector(sect); 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci if (pvd) { 22462306a36Sopenharmony_ci int numpps = be16_to_cpu(pvd->pp_count); 22562306a36Sopenharmony_ci int psn_part1 = be32_to_cpu(pvd->psn_part1); 22662306a36Sopenharmony_ci int i; 22762306a36Sopenharmony_ci int cur_lv_ix = -1; 22862306a36Sopenharmony_ci int next_lp_ix = 1; 22962306a36Sopenharmony_ci int lp_ix; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci for (i = 0; i < numpps; i += 1) { 23262306a36Sopenharmony_ci struct ppe *p = pvd->ppe + i; 23362306a36Sopenharmony_ci unsigned int lv_ix; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci lp_ix = be16_to_cpu(p->lp_ix); 23662306a36Sopenharmony_ci if (!lp_ix) { 23762306a36Sopenharmony_ci next_lp_ix = 1; 23862306a36Sopenharmony_ci continue; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci lv_ix = be16_to_cpu(p->lv_ix) - 1; 24162306a36Sopenharmony_ci if (lv_ix >= state->limit) { 24262306a36Sopenharmony_ci cur_lv_ix = -1; 24362306a36Sopenharmony_ci continue; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci lvip[lv_ix].pps_found += 1; 24662306a36Sopenharmony_ci if (lp_ix == 1) { 24762306a36Sopenharmony_ci cur_lv_ix = lv_ix; 24862306a36Sopenharmony_ci next_lp_ix = 1; 24962306a36Sopenharmony_ci } else if (lv_ix != cur_lv_ix || lp_ix != next_lp_ix) { 25062306a36Sopenharmony_ci next_lp_ix = 1; 25162306a36Sopenharmony_ci continue; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci if (lp_ix == lvip[lv_ix].pps_per_lv) { 25462306a36Sopenharmony_ci char tmp[70]; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci put_partition(state, lv_ix + 1, 25762306a36Sopenharmony_ci (i + 1 - lp_ix) * pp_blocks_size + psn_part1, 25862306a36Sopenharmony_ci lvip[lv_ix].pps_per_lv * pp_blocks_size); 25962306a36Sopenharmony_ci snprintf(tmp, sizeof(tmp), " <%s>\n", 26062306a36Sopenharmony_ci n[lv_ix].name); 26162306a36Sopenharmony_ci strlcat(state->pp_buf, tmp, PAGE_SIZE); 26262306a36Sopenharmony_ci lvip[lv_ix].lv_is_contiguous = 1; 26362306a36Sopenharmony_ci ret = 1; 26462306a36Sopenharmony_ci next_lp_ix = 1; 26562306a36Sopenharmony_ci } else 26662306a36Sopenharmony_ci next_lp_ix += 1; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci for (i = 0; i < state->limit; i += 1) 26962306a36Sopenharmony_ci if (lvip[i].pps_found && !lvip[i].lv_is_contiguous) { 27062306a36Sopenharmony_ci char tmp[sizeof(n[i].name) + 1]; // null char 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci snprintf(tmp, sizeof(tmp), "%s", n[i].name); 27362306a36Sopenharmony_ci pr_warn("partition %s (%u pp's found) is " 27462306a36Sopenharmony_ci "not contiguous\n", 27562306a36Sopenharmony_ci tmp, lvip[i].pps_found); 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci kfree(pvd); 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci kfree(n); 28062306a36Sopenharmony_ci kfree(lvip); 28162306a36Sopenharmony_ci return ret; 28262306a36Sopenharmony_ci} 283