18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * fs/partitions/aix.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012-2013 Philippe De Muyter <phdm@macqel.be> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include "check.h" 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_cistruct lvm_rec { 118c2ecf20Sopenharmony_ci char lvm_id[4]; /* "_LVM" */ 128c2ecf20Sopenharmony_ci char reserved4[16]; 138c2ecf20Sopenharmony_ci __be32 lvmarea_len; 148c2ecf20Sopenharmony_ci __be32 vgda_len; 158c2ecf20Sopenharmony_ci __be32 vgda_psn[2]; 168c2ecf20Sopenharmony_ci char reserved36[10]; 178c2ecf20Sopenharmony_ci __be16 pp_size; /* log2(pp_size) */ 188c2ecf20Sopenharmony_ci char reserved46[12]; 198c2ecf20Sopenharmony_ci __be16 version; 208c2ecf20Sopenharmony_ci }; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistruct vgda { 238c2ecf20Sopenharmony_ci __be32 secs; 248c2ecf20Sopenharmony_ci __be32 usec; 258c2ecf20Sopenharmony_ci char reserved8[16]; 268c2ecf20Sopenharmony_ci __be16 numlvs; 278c2ecf20Sopenharmony_ci __be16 maxlvs; 288c2ecf20Sopenharmony_ci __be16 pp_size; 298c2ecf20Sopenharmony_ci __be16 numpvs; 308c2ecf20Sopenharmony_ci __be16 total_vgdas; 318c2ecf20Sopenharmony_ci __be16 vgda_size; 328c2ecf20Sopenharmony_ci }; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistruct lvd { 358c2ecf20Sopenharmony_ci __be16 lv_ix; 368c2ecf20Sopenharmony_ci __be16 res2; 378c2ecf20Sopenharmony_ci __be16 res4; 388c2ecf20Sopenharmony_ci __be16 maxsize; 398c2ecf20Sopenharmony_ci __be16 lv_state; 408c2ecf20Sopenharmony_ci __be16 mirror; 418c2ecf20Sopenharmony_ci __be16 mirror_policy; 428c2ecf20Sopenharmony_ci __be16 num_lps; 438c2ecf20Sopenharmony_ci __be16 res10[8]; 448c2ecf20Sopenharmony_ci }; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct lvname { 478c2ecf20Sopenharmony_ci char name[64]; 488c2ecf20Sopenharmony_ci }; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct ppe { 518c2ecf20Sopenharmony_ci __be16 lv_ix; 528c2ecf20Sopenharmony_ci unsigned short res2; 538c2ecf20Sopenharmony_ci unsigned short res4; 548c2ecf20Sopenharmony_ci __be16 lp_ix; 558c2ecf20Sopenharmony_ci unsigned short res8[12]; 568c2ecf20Sopenharmony_ci }; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistruct pvd { 598c2ecf20Sopenharmony_ci char reserved0[16]; 608c2ecf20Sopenharmony_ci __be16 pp_count; 618c2ecf20Sopenharmony_ci char reserved18[2]; 628c2ecf20Sopenharmony_ci __be32 psn_part1; 638c2ecf20Sopenharmony_ci char reserved24[8]; 648c2ecf20Sopenharmony_ci struct ppe ppe[1016]; 658c2ecf20Sopenharmony_ci }; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define LVM_MAXLVS 256 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/** 708c2ecf20Sopenharmony_ci * last_lba(): return number of last logical block of device 718c2ecf20Sopenharmony_ci * @bdev: block device 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * Description: Returns last LBA value on success, 0 on error. 748c2ecf20Sopenharmony_ci * This is stored (by sd and ide-geometry) in 758c2ecf20Sopenharmony_ci * the part[0] entry for this disk, and is the number of 768c2ecf20Sopenharmony_ci * physical sectors available on the disk. 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_cistatic u64 last_lba(struct block_device *bdev) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci if (!bdev || !bdev->bd_inode) 818c2ecf20Sopenharmony_ci return 0; 828c2ecf20Sopenharmony_ci return (bdev->bd_inode->i_size >> 9) - 1ULL; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/** 868c2ecf20Sopenharmony_ci * read_lba(): Read bytes from disk, starting at given LBA 878c2ecf20Sopenharmony_ci * @state 888c2ecf20Sopenharmony_ci * @lba 898c2ecf20Sopenharmony_ci * @buffer 908c2ecf20Sopenharmony_ci * @count 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * Description: Reads @count bytes from @state->bdev into @buffer. 938c2ecf20Sopenharmony_ci * Returns number of bytes read on success, 0 on error. 948c2ecf20Sopenharmony_ci */ 958c2ecf20Sopenharmony_cistatic size_t read_lba(struct parsed_partitions *state, u64 lba, u8 *buffer, 968c2ecf20Sopenharmony_ci size_t count) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci size_t totalreadcount = 0; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (!buffer || lba + count / 512 > last_lba(state->bdev)) 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci while (count) { 1048c2ecf20Sopenharmony_ci int copied = 512; 1058c2ecf20Sopenharmony_ci Sector sect; 1068c2ecf20Sopenharmony_ci unsigned char *data = read_part_sector(state, lba++, §); 1078c2ecf20Sopenharmony_ci if (!data) 1088c2ecf20Sopenharmony_ci break; 1098c2ecf20Sopenharmony_ci if (copied > count) 1108c2ecf20Sopenharmony_ci copied = count; 1118c2ecf20Sopenharmony_ci memcpy(buffer, data, copied); 1128c2ecf20Sopenharmony_ci put_dev_sector(sect); 1138c2ecf20Sopenharmony_ci buffer += copied; 1148c2ecf20Sopenharmony_ci totalreadcount += copied; 1158c2ecf20Sopenharmony_ci count -= copied; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci return totalreadcount; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/** 1218c2ecf20Sopenharmony_ci * alloc_pvd(): reads physical volume descriptor 1228c2ecf20Sopenharmony_ci * @state 1238c2ecf20Sopenharmony_ci * @lba 1248c2ecf20Sopenharmony_ci * 1258c2ecf20Sopenharmony_ci * Description: Returns pvd on success, NULL on error. 1268c2ecf20Sopenharmony_ci * Allocates space for pvd and fill it with disk blocks at @lba 1278c2ecf20Sopenharmony_ci * Notes: remember to free pvd when you're done! 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_cistatic struct pvd *alloc_pvd(struct parsed_partitions *state, u32 lba) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci size_t count = sizeof(struct pvd); 1328c2ecf20Sopenharmony_ci struct pvd *p; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci p = kmalloc(count, GFP_KERNEL); 1358c2ecf20Sopenharmony_ci if (!p) 1368c2ecf20Sopenharmony_ci return NULL; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (read_lba(state, lba, (u8 *) p, count) < count) { 1398c2ecf20Sopenharmony_ci kfree(p); 1408c2ecf20Sopenharmony_ci return NULL; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci return p; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/** 1468c2ecf20Sopenharmony_ci * alloc_lvn(): reads logical volume names 1478c2ecf20Sopenharmony_ci * @state 1488c2ecf20Sopenharmony_ci * @lba 1498c2ecf20Sopenharmony_ci * 1508c2ecf20Sopenharmony_ci * Description: Returns lvn on success, NULL on error. 1518c2ecf20Sopenharmony_ci * Allocates space for lvn and fill it with disk blocks at @lba 1528c2ecf20Sopenharmony_ci * Notes: remember to free lvn when you're done! 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_cistatic struct lvname *alloc_lvn(struct parsed_partitions *state, u32 lba) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci size_t count = sizeof(struct lvname) * LVM_MAXLVS; 1578c2ecf20Sopenharmony_ci struct lvname *p; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci p = kmalloc(count, GFP_KERNEL); 1608c2ecf20Sopenharmony_ci if (!p) 1618c2ecf20Sopenharmony_ci return NULL; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (read_lba(state, lba, (u8 *) p, count) < count) { 1648c2ecf20Sopenharmony_ci kfree(p); 1658c2ecf20Sopenharmony_ci return NULL; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci return p; 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ciint aix_partition(struct parsed_partitions *state) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci int ret = 0; 1738c2ecf20Sopenharmony_ci Sector sect; 1748c2ecf20Sopenharmony_ci unsigned char *d; 1758c2ecf20Sopenharmony_ci u32 pp_bytes_size; 1768c2ecf20Sopenharmony_ci u32 pp_blocks_size = 0; 1778c2ecf20Sopenharmony_ci u32 vgda_sector = 0; 1788c2ecf20Sopenharmony_ci u32 vgda_len = 0; 1798c2ecf20Sopenharmony_ci int numlvs = 0; 1808c2ecf20Sopenharmony_ci struct pvd *pvd = NULL; 1818c2ecf20Sopenharmony_ci struct lv_info { 1828c2ecf20Sopenharmony_ci unsigned short pps_per_lv; 1838c2ecf20Sopenharmony_ci unsigned short pps_found; 1848c2ecf20Sopenharmony_ci unsigned char lv_is_contiguous; 1858c2ecf20Sopenharmony_ci } *lvip; 1868c2ecf20Sopenharmony_ci struct lvname *n = NULL; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci d = read_part_sector(state, 7, §); 1898c2ecf20Sopenharmony_ci if (d) { 1908c2ecf20Sopenharmony_ci struct lvm_rec *p = (struct lvm_rec *)d; 1918c2ecf20Sopenharmony_ci u16 lvm_version = be16_to_cpu(p->version); 1928c2ecf20Sopenharmony_ci char tmp[64]; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (lvm_version == 1) { 1958c2ecf20Sopenharmony_ci int pp_size_log2 = be16_to_cpu(p->pp_size); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci pp_bytes_size = 1 << pp_size_log2; 1988c2ecf20Sopenharmony_ci pp_blocks_size = pp_bytes_size / 512; 1998c2ecf20Sopenharmony_ci snprintf(tmp, sizeof(tmp), 2008c2ecf20Sopenharmony_ci " AIX LVM header version %u found\n", 2018c2ecf20Sopenharmony_ci lvm_version); 2028c2ecf20Sopenharmony_ci vgda_len = be32_to_cpu(p->vgda_len); 2038c2ecf20Sopenharmony_ci vgda_sector = be32_to_cpu(p->vgda_psn[0]); 2048c2ecf20Sopenharmony_ci } else { 2058c2ecf20Sopenharmony_ci snprintf(tmp, sizeof(tmp), 2068c2ecf20Sopenharmony_ci " unsupported AIX LVM version %d found\n", 2078c2ecf20Sopenharmony_ci lvm_version); 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci strlcat(state->pp_buf, tmp, PAGE_SIZE); 2108c2ecf20Sopenharmony_ci put_dev_sector(sect); 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci if (vgda_sector && (d = read_part_sector(state, vgda_sector, §))) { 2138c2ecf20Sopenharmony_ci struct vgda *p = (struct vgda *)d; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci numlvs = be16_to_cpu(p->numlvs); 2168c2ecf20Sopenharmony_ci put_dev_sector(sect); 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci lvip = kcalloc(state->limit, sizeof(struct lv_info), GFP_KERNEL); 2198c2ecf20Sopenharmony_ci if (!lvip) 2208c2ecf20Sopenharmony_ci return 0; 2218c2ecf20Sopenharmony_ci if (numlvs && (d = read_part_sector(state, vgda_sector + 1, §))) { 2228c2ecf20Sopenharmony_ci struct lvd *p = (struct lvd *)d; 2238c2ecf20Sopenharmony_ci int i; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci n = alloc_lvn(state, vgda_sector + vgda_len - 33); 2268c2ecf20Sopenharmony_ci if (n) { 2278c2ecf20Sopenharmony_ci int foundlvs = 0; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci for (i = 0; foundlvs < numlvs && i < state->limit; i += 1) { 2308c2ecf20Sopenharmony_ci lvip[i].pps_per_lv = be16_to_cpu(p[i].num_lps); 2318c2ecf20Sopenharmony_ci if (lvip[i].pps_per_lv) 2328c2ecf20Sopenharmony_ci foundlvs += 1; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci /* pvd loops depend on n[].name and lvip[].pps_per_lv */ 2358c2ecf20Sopenharmony_ci pvd = alloc_pvd(state, vgda_sector + 17); 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci put_dev_sector(sect); 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci if (pvd) { 2408c2ecf20Sopenharmony_ci int numpps = be16_to_cpu(pvd->pp_count); 2418c2ecf20Sopenharmony_ci int psn_part1 = be32_to_cpu(pvd->psn_part1); 2428c2ecf20Sopenharmony_ci int i; 2438c2ecf20Sopenharmony_ci int cur_lv_ix = -1; 2448c2ecf20Sopenharmony_ci int next_lp_ix = 1; 2458c2ecf20Sopenharmony_ci int lp_ix; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci for (i = 0; i < numpps; i += 1) { 2488c2ecf20Sopenharmony_ci struct ppe *p = pvd->ppe + i; 2498c2ecf20Sopenharmony_ci unsigned int lv_ix; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci lp_ix = be16_to_cpu(p->lp_ix); 2528c2ecf20Sopenharmony_ci if (!lp_ix) { 2538c2ecf20Sopenharmony_ci next_lp_ix = 1; 2548c2ecf20Sopenharmony_ci continue; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci lv_ix = be16_to_cpu(p->lv_ix) - 1; 2578c2ecf20Sopenharmony_ci if (lv_ix >= state->limit) { 2588c2ecf20Sopenharmony_ci cur_lv_ix = -1; 2598c2ecf20Sopenharmony_ci continue; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci lvip[lv_ix].pps_found += 1; 2628c2ecf20Sopenharmony_ci if (lp_ix == 1) { 2638c2ecf20Sopenharmony_ci cur_lv_ix = lv_ix; 2648c2ecf20Sopenharmony_ci next_lp_ix = 1; 2658c2ecf20Sopenharmony_ci } else if (lv_ix != cur_lv_ix || lp_ix != next_lp_ix) { 2668c2ecf20Sopenharmony_ci next_lp_ix = 1; 2678c2ecf20Sopenharmony_ci continue; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci if (lp_ix == lvip[lv_ix].pps_per_lv) { 2708c2ecf20Sopenharmony_ci char tmp[70]; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci put_partition(state, lv_ix + 1, 2738c2ecf20Sopenharmony_ci (i + 1 - lp_ix) * pp_blocks_size + psn_part1, 2748c2ecf20Sopenharmony_ci lvip[lv_ix].pps_per_lv * pp_blocks_size); 2758c2ecf20Sopenharmony_ci snprintf(tmp, sizeof(tmp), " <%s>\n", 2768c2ecf20Sopenharmony_ci n[lv_ix].name); 2778c2ecf20Sopenharmony_ci strlcat(state->pp_buf, tmp, PAGE_SIZE); 2788c2ecf20Sopenharmony_ci lvip[lv_ix].lv_is_contiguous = 1; 2798c2ecf20Sopenharmony_ci ret = 1; 2808c2ecf20Sopenharmony_ci next_lp_ix = 1; 2818c2ecf20Sopenharmony_ci } else 2828c2ecf20Sopenharmony_ci next_lp_ix += 1; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci for (i = 0; i < state->limit; i += 1) 2858c2ecf20Sopenharmony_ci if (lvip[i].pps_found && !lvip[i].lv_is_contiguous) { 2868c2ecf20Sopenharmony_ci char tmp[sizeof(n[i].name) + 1]; // null char 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci snprintf(tmp, sizeof(tmp), "%s", n[i].name); 2898c2ecf20Sopenharmony_ci pr_warn("partition %s (%u pp's found) is " 2908c2ecf20Sopenharmony_ci "not contiguous\n", 2918c2ecf20Sopenharmony_ci tmp, lvip[i].pps_found); 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci kfree(pvd); 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci kfree(n); 2968c2ecf20Sopenharmony_ci kfree(lvip); 2978c2ecf20Sopenharmony_ci return ret; 2988c2ecf20Sopenharmony_ci} 299