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++, &sect);
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, &sect);
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, &sect))) {
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, &sect))) {
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