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