18dab5b0mav/*-
2a82e3a8pfg * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3a82e3a8pfg *
48dab5b0mav * Copyright (c) 2010 Alexander Motin <mav@FreeBSD.org>
5c283985mav * Copyright (c) 2000 - 2008 S��ren Schmidt <sos@FreeBSD.org>
68dab5b0mav * All rights reserved.
78dab5b0mav *
88dab5b0mav * Redistribution and use in source and binary forms, with or without
98dab5b0mav * modification, are permitted provided that the following conditions
108dab5b0mav * are met:
118dab5b0mav * 1. Redistributions of source code must retain the above copyright
128dab5b0mav *    notice, this list of conditions and the following disclaimer.
138dab5b0mav * 2. Redistributions in binary form must reproduce the above copyright
148dab5b0mav *    notice, this list of conditions and the following disclaimer in the
158dab5b0mav *    documentation and/or other materials provided with the distribution.
168dab5b0mav *
178dab5b0mav * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
188dab5b0mav * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
198dab5b0mav * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
208dab5b0mav * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
218dab5b0mav * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
228dab5b0mav * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
238dab5b0mav * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
248dab5b0mav * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
258dab5b0mav * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
268dab5b0mav * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
278dab5b0mav * SUCH DAMAGE.
288dab5b0mav */
298dab5b0mav
308dab5b0mav#include <sys/cdefs.h>
318dab5b0mav__FBSDID("$FreeBSD$");
328dab5b0mav
338dab5b0mav#include <sys/param.h>
348dab5b0mav#include <sys/bio.h>
358dab5b0mav#include <sys/endian.h>
368dab5b0mav#include <sys/kernel.h>
378dab5b0mav#include <sys/kobj.h>
388dab5b0mav#include <sys/limits.h>
398dab5b0mav#include <sys/lock.h>
408dab5b0mav#include <sys/malloc.h>
418dab5b0mav#include <sys/mutex.h>
428dab5b0mav#include <sys/systm.h>
438dab5b0mav#include <sys/taskqueue.h>
448dab5b0mav#include <geom/geom.h>
4510d53fccem#include <geom/geom_dbg.h>
468dab5b0mav#include "geom/raid/g_raid.h"
478dab5b0mav#include "g_raid_md_if.h"
488dab5b0mav
498dab5b0mavstatic MALLOC_DEFINE(M_MD_JMICRON, "md_jmicron_data", "GEOM_RAID JMicron metadata");
508dab5b0mav
518dab5b0mav#define	JMICRON_MAX_DISKS	8
528dab5b0mav#define	JMICRON_MAX_SPARE	2
538dab5b0mav
548dab5b0mavstruct jmicron_raid_conf {
558dab5b0mav    u_int8_t		signature[2];
568dab5b0mav#define	JMICRON_MAGIC		"JM"
578dab5b0mav
588dab5b0mav    u_int16_t		version;
598dab5b0mav#define	JMICRON_VERSION		0x0001
608dab5b0mav
618dab5b0mav    u_int16_t		checksum;
628dab5b0mav    u_int8_t		filler_1[10];
638dab5b0mav    u_int32_t		disk_id;
648dab5b0mav    u_int32_t		offset;
658dab5b0mav    u_int32_t		disk_sectors_high;
668dab5b0mav    u_int16_t		disk_sectors_low;
678dab5b0mav    u_int8_t		filler_2[2];
688dab5b0mav    u_int8_t		name[16];
698dab5b0mav    u_int8_t		type;
708dab5b0mav#define	JMICRON_T_RAID0		0
718dab5b0mav#define	JMICRON_T_RAID1		1
728dab5b0mav#define	JMICRON_T_RAID01	2
738dab5b0mav#define	JMICRON_T_CONCAT	3
748dab5b0mav#define	JMICRON_T_RAID5		5
758dab5b0mav
768dab5b0mav    u_int8_t		stripe_shift;
778dab5b0mav    u_int16_t		flags;
788dab5b0mav#define	JMICRON_F_READY		0x0001
798dab5b0mav#define	JMICRON_F_BOOTABLE	0x0002
808dab5b0mav#define	JMICRON_F_BADSEC	0x0004
818dab5b0mav#define	JMICRON_F_ACTIVE	0x0010
828dab5b0mav#define	JMICRON_F_UNSYNC	0x0020
838dab5b0mav#define	JMICRON_F_NEWEST	0x0040
848dab5b0mav
858dab5b0mav    u_int8_t		filler_3[4];
868dab5b0mav    u_int32_t		spare[JMICRON_MAX_SPARE];
878dab5b0mav    u_int32_t		disks[JMICRON_MAX_DISKS];
888dab5b0mav#define	JMICRON_DISK_MASK	0xFFFFFFF0
898dab5b0mav#define	JMICRON_SEG_MASK	0x0000000F
908dab5b0mav    u_int8_t		filler_4[32];
918dab5b0mav    u_int8_t		filler_5[384];
928dab5b0mav};
938dab5b0mav
948dab5b0mavstruct g_raid_md_jmicron_perdisk {
958dab5b0mav	struct jmicron_raid_conf	*pd_meta;
968dab5b0mav	int				 pd_disk_pos;
978dab5b0mav	int				 pd_disk_id;
988dab5b0mav	off_t				 pd_disk_size;
998dab5b0mav};
1008dab5b0mav
1018dab5b0mavstruct g_raid_md_jmicron_object {
1028dab5b0mav	struct g_raid_md_object	 mdio_base;
1038dab5b0mav	uint32_t		 mdio_config_id;
1048dab5b0mav	struct jmicron_raid_conf	*mdio_meta;
1058dab5b0mav	struct callout		 mdio_start_co;	/* STARTING state timer. */
1068dab5b0mav	int			 mdio_total_disks;
1078dab5b0mav	int			 mdio_disks_present;
1088dab5b0mav	int			 mdio_started;
1098dab5b0mav	int			 mdio_incomplete;
1108dab5b0mav	struct root_hold_token	*mdio_rootmount; /* Root mount delay token. */
1118dab5b0mav};
1128dab5b0mav
1138dab5b0mavstatic g_raid_md_create_t g_raid_md_create_jmicron;
1148dab5b0mavstatic g_raid_md_taste_t g_raid_md_taste_jmicron;
1158dab5b0mavstatic g_raid_md_event_t g_raid_md_event_jmicron;
1168dab5b0mavstatic g_raid_md_ctl_t g_raid_md_ctl_jmicron;
1178dab5b0mavstatic g_raid_md_write_t g_raid_md_write_jmicron;
1188dab5b0mavstatic g_raid_md_fail_disk_t g_raid_md_fail_disk_jmicron;
1198dab5b0mavstatic g_raid_md_free_disk_t g_raid_md_free_disk_jmicron;
1208dab5b0mavstatic g_raid_md_free_t g_raid_md_free_jmicron;
1218dab5b0mav
1228dab5b0mavstatic kobj_method_t g_raid_md_jmicron_methods[] = {
1238dab5b0mav	KOBJMETHOD(g_raid_md_create,	g_raid_md_create_jmicron),
1248dab5b0mav	KOBJMETHOD(g_raid_md_taste,	g_raid_md_taste_jmicron),
1258dab5b0mav	KOBJMETHOD(g_raid_md_event,	g_raid_md_event_jmicron),
1268dab5b0mav	KOBJMETHOD(g_raid_md_ctl,	g_raid_md_ctl_jmicron),
1278dab5b0mav	KOBJMETHOD(g_raid_md_write,	g_raid_md_write_jmicron),
1288dab5b0mav	KOBJMETHOD(g_raid_md_fail_disk,	g_raid_md_fail_disk_jmicron),
1298dab5b0mav	KOBJMETHOD(g_raid_md_free_disk,	g_raid_md_free_disk_jmicron),
1308dab5b0mav	KOBJMETHOD(g_raid_md_free,	g_raid_md_free_jmicron),
1318dab5b0mav	{ 0, 0 }
1328dab5b0mav};
1338dab5b0mav
1348dab5b0mavstatic struct g_raid_md_class g_raid_md_jmicron_class = {
1358dab5b0mav	"JMicron",
1368dab5b0mav	g_raid_md_jmicron_methods,
1378dab5b0mav	sizeof(struct g_raid_md_jmicron_object),
138db9e01amav	.mdc_enable = 1,
1398dab5b0mav	.mdc_priority = 100
1408dab5b0mav};
1418dab5b0mav
1428dab5b0mavstatic void
1438dab5b0mavg_raid_md_jmicron_print(struct jmicron_raid_conf *meta)
1448dab5b0mav{
1458dab5b0mav	int k;
1468dab5b0mav
1478dab5b0mav	if (g_raid_debug < 1)
1488dab5b0mav		return;
1498dab5b0mav
1508dab5b0mav	printf("********* ATA JMicron RAID Metadata *********\n");
1518dab5b0mav	printf("signature           <%c%c>\n", meta->signature[0], meta->signature[1]);
1528dab5b0mav	printf("version             %04x\n", meta->version);
1538dab5b0mav	printf("checksum            0x%04x\n", meta->checksum);
1548dab5b0mav	printf("disk_id             0x%08x\n", meta->disk_id);
1558dab5b0mav	printf("offset              0x%08x\n", meta->offset);
1568dab5b0mav	printf("disk_sectors_high   0x%08x\n", meta->disk_sectors_high);
1578dab5b0mav	printf("disk_sectors_low    0x%04x\n", meta->disk_sectors_low);
1588dab5b0mav	printf("name                <%.16s>\n", meta->name);
1598dab5b0mav	printf("type                %d\n", meta->type);
1608dab5b0mav	printf("stripe_shift        %d\n", meta->stripe_shift);
1618dab5b0mav	printf("flags               %04x\n", meta->flags);
1628dab5b0mav	printf("spare              ");
1638dab5b0mav	for (k = 0; k < JMICRON_MAX_SPARE; k++)
1648dab5b0mav		printf(" 0x%08x", meta->spare[k]);
1658dab5b0mav	printf("\n");
1668dab5b0mav	printf("disks              ");
1678dab5b0mav	for (k = 0; k < JMICRON_MAX_DISKS; k++)
1688dab5b0mav		printf(" 0x%08x", meta->disks[k]);
1698dab5b0mav	printf("\n");
1708dab5b0mav	printf("=================================================\n");
1718dab5b0mav}
1728dab5b0mav
1738dab5b0mavstatic struct jmicron_raid_conf *
1748dab5b0mavjmicron_meta_copy(struct jmicron_raid_conf *meta)
1758dab5b0mav{
1768dab5b0mav	struct jmicron_raid_conf *nmeta;
1778dab5b0mav
1788dab5b0mav	nmeta = malloc(sizeof(*meta), M_MD_JMICRON, M_WAITOK);
1798dab5b0mav	memcpy(nmeta, meta, sizeof(*meta));
1808dab5b0mav	return (nmeta);
1818dab5b0mav}
1828dab5b0mav
1838dab5b0mavstatic int
1848dab5b0mavjmicron_meta_total_disks(struct jmicron_raid_conf *meta)
1858dab5b0mav{
1868dab5b0mav	int pos;
1878dab5b0mav
1888dab5b0mav	for (pos = 0; pos < JMICRON_MAX_DISKS; pos++) {
1898dab5b0mav		if (meta->disks[pos] == 0)
1908dab5b0mav			break;
1918dab5b0mav	}
1928dab5b0mav	return (pos);
1938dab5b0mav}
1948dab5b0mav
1958dab5b0mavstatic int
1968dab5b0mavjmicron_meta_total_spare(struct jmicron_raid_conf *meta)
1978dab5b0mav{
1988dab5b0mav	int pos, n;
1998dab5b0mav
2008dab5b0mav	n = 0;
2018dab5b0mav	for (pos = 0; pos < JMICRON_MAX_SPARE; pos++) {
2028dab5b0mav		if (meta->spare[pos] != 0)
2038dab5b0mav			n++;
2048dab5b0mav	}
2058dab5b0mav	return (n);
2068dab5b0mav}
2078dab5b0mav
2088dab5b0mav/*
2098dab5b0mav * Generate fake Configuration ID based on disk IDs.
2108dab5b0mav * Note: it will change after each disk set change.
2118dab5b0mav */
2128dab5b0mavstatic uint32_t
2138dab5b0mavjmicron_meta_config_id(struct jmicron_raid_conf *meta)
2148dab5b0mav{
2158dab5b0mav	int pos;
2168dab5b0mav	uint32_t config_id;
2178dab5b0mav
2188dab5b0mav	config_id = 0;
2198dab5b0mav	for (pos = 0; pos < JMICRON_MAX_DISKS; pos++)
2208dab5b0mav		config_id += meta->disks[pos] << pos;
2218dab5b0mav	return (config_id);
2228dab5b0mav}
2238dab5b0mav
2248dab5b0mavstatic void
2258dab5b0mavjmicron_meta_get_name(struct jmicron_raid_conf *meta, char *buf)
2268dab5b0mav{
2278dab5b0mav	int i;
2288dab5b0mav
2298dab5b0mav	strncpy(buf, meta->name, 16);
2308dab5b0mav	buf[16] = 0;
2318dab5b0mav	for (i = 15; i >= 0; i--) {
2328dab5b0mav		if (buf[i] > 0x20)
2338dab5b0mav			break;
2348dab5b0mav		buf[i] = 0;
2358dab5b0mav	}
2368dab5b0mav}
2378dab5b0mav
2388dab5b0mavstatic void
2398dab5b0mavjmicron_meta_put_name(struct jmicron_raid_conf *meta, char *buf)
2408dab5b0mav{
2418dab5b0mav
2428dab5b0mav	memset(meta->name, 0x20, 16);
2438dab5b0mav	memcpy(meta->name, buf, MIN(strlen(buf), 16));
2448dab5b0mav}
2458dab5b0mav
2468dab5b0mavstatic int
2478dab5b0mavjmicron_meta_find_disk(struct jmicron_raid_conf *meta, uint32_t id)
2488dab5b0mav{
2498dab5b0mav	int pos;
2508dab5b0mav
2518dab5b0mav	id &= JMICRON_DISK_MASK;
2528dab5b0mav	for (pos = 0; pos < JMICRON_MAX_DISKS; pos++) {
2538dab5b0mav		if ((meta->disks[pos] & JMICRON_DISK_MASK) == id)
2548dab5b0mav			return (pos);
2558dab5b0mav	}
2568dab5b0mav	for (pos = 0; pos < JMICRON_MAX_SPARE; pos++) {
2578dab5b0mav		if ((meta->spare[pos] & JMICRON_DISK_MASK) == id)
2588dab5b0mav			return (-3);
2598dab5b0mav	}
2608dab5b0mav	return (-1);
2618dab5b0mav}
2628dab5b0mav
2638dab5b0mavstatic struct jmicron_raid_conf *
2648dab5b0mavjmicron_meta_read(struct g_consumer *cp)
2658dab5b0mav{
2668dab5b0mav	struct g_provider *pp;
2678dab5b0mav	struct jmicron_raid_conf *meta;
2688dab5b0mav	char *buf;
2698dab5b0mav	int error, i;
2708dab5b0mav	uint16_t checksum, *ptr;
2718dab5b0mav
2728dab5b0mav	pp = cp->provider;
2738dab5b0mav
2748dab5b0mav	/* Read the anchor sector. */
2758dab5b0mav	buf = g_read_data(cp,
2768dab5b0mav	    pp->mediasize - pp->sectorsize, pp->sectorsize, &error);
2778dab5b0mav	if (buf == NULL) {
2788dab5b0mav		G_RAID_DEBUG(1, "Cannot read metadata from %s (error=%d).",
2798dab5b0mav		    pp->name, error);
2808dab5b0mav		return (NULL);
2818dab5b0mav	}
2828dab5b0mav	meta = (struct jmicron_raid_conf *)buf;
2838dab5b0mav
2848dab5b0mav	/* Check if this is an JMicron RAID struct */
2858dab5b0mav	if (strncmp(meta->signature, JMICRON_MAGIC, strlen(JMICRON_MAGIC))) {
2868dab5b0mav		G_RAID_DEBUG(1, "JMicron signature check failed on %s", pp->name);
2878dab5b0mav		g_free(buf);
2888dab5b0mav		return (NULL);
2898dab5b0mav	}
2908dab5b0mav	meta = malloc(sizeof(*meta), M_MD_JMICRON, M_WAITOK);
2918dab5b0mav	memcpy(meta, buf, min(sizeof(*meta), pp->sectorsize));
2928dab5b0mav	g_free(buf);
2938dab5b0mav
2948dab5b0mav	/* Check metadata checksum. */
2958dab5b0mav	for (checksum = 0, ptr = (uint16_t *)meta, i = 0; i < 64; i++)
2968dab5b0mav		checksum += *ptr++;
297