pkgsrc-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: pkg/57076: Add efivar port to NetBSD



The following reply was made to PR pkg/57076; it has been noted by GNATS.

From: Sergii Dmytruk <sergii.dmytruk%3mdeb.com@localhost>
To: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Cc: gnats-bugs%NetBSD.org@localhost
Subject: Re: pkg/57076: Add efivar port to NetBSD
Date: Sun, 6 Nov 2022 19:42:24 +0200

 --e94Sh53BmyHhQjjd
 Content-Type: text/plain; charset=us-ascii
 Content-Disposition: inline
 
 Apart from swapped arguments, there was incorrect duplicated definition of
 EFI_SUCCESS:
 
     #define	EFI_SUCCESS		EFIERR(0)
 
 It's just 0, since this isn't an error.
 
 With these fixes, everything works.  I've attached modified patch and a
 client for the API which I used for testing.
 
 --e94Sh53BmyHhQjjd
 Content-Type: text/plain; charset=us-ascii
 Content-Disposition: attachment; filename="efitab-fixed.patch"
 
  sys/arch/x86/x86/efi_machdep.c |  29 +++---
  sys/dev/efi.c                  | 216 ++++++++++++++++++++++++++++++++++++++---
  sys/dev/efi/efi.h              |  16 +++
  sys/dev/efivar.h               |   8 +-
  sys/sys/efiio.h                |   8 ++
  5 files changed, 247 insertions(+), 30 deletions(-)
 
 diff --git a/sys/arch/x86/x86/efi_machdep.c b/sys/arch/x86/x86/efi_machdep.c
 index 3a40a8eee04..57710c799dd 100644
 --- a/sys/arch/x86/x86/efi_machdep.c
 +++ b/sys/arch/x86/x86/efi_machdep.c
 @@ -581,18 +581,6 @@ efi_get_e820memmap(void)
  
  #ifdef EFI_RUNTIME
  
 -/*
 - * XXX move to sys/dev/efi/efi.h
 - */
 -#ifdef _LP64
 -#define	EFIERR(x)	(0x8000000000000000ul | (x))
 -#else
 -#define	EFIERR(x)	(0x80000000ul | (x))
 -#endif
 -
 -#define	EFI_UNSUPPORTED		EFIERR(3)
 -#define	EFI_DEVICE_ERROR	EFIERR(7)
 -
  /*
   * efi_runtime_init()
   *
 @@ -985,12 +973,29 @@ efi_runtime_setvar(efi_char *name, struct uuid *vendor, uint32_t attrib,
  	return status;
  }
  
 +static efi_status
 +efi_runtime_gettab(const struct uuid *vendor, uint64_t *addrp)
 +{
 +	struct efi_cfgtbl *cfgtbl = efi_getcfgtblhead();
 +	paddr_t pa;
 +
 +	if (cfgtbl == NULL)
 +		return EFI_UNSUPPORTED;
 +
 +	pa = efi_getcfgtblpa(vendor);
 +	if (pa == 0)
 +		return EFI_NOT_FOUND;
 +	*addrp = pa;
 +	return EFI_SUCCESS;
 +}
 +
  static struct efi_ops efi_runtime_ops = {
  	.efi_gettime = efi_runtime_gettime,
  	.efi_settime = efi_runtime_settime,
  	.efi_getvar = efi_runtime_getvar,
  	.efi_setvar = efi_runtime_setvar,
  	.efi_nextvar = efi_runtime_nextvar,
 +	.efi_gettab = efi_runtime_gettab,
  };
  
  #endif	/* EFI_RUNTIME */
 diff --git a/sys/dev/efi.c b/sys/dev/efi.c
 index 91f0e1365d7..cba8c0f3707 100644
 --- a/sys/dev/efi.c
 +++ b/sys/dev/efi.c
 @@ -40,23 +40,10 @@ __KERNEL_RCSID(0, "$NetBSD: efi.c,v 1.4 2022/09/24 11:06:03 riastradh Exp $");
  #include <sys/atomic.h>
  #include <sys/efiio.h>
  
 -#include <dev/efivar.h>
 -
 -#ifdef _LP64
 -#define	EFIERR(x)		(0x8000000000000000 | x)
 -#else
 -#define	EFIERR(x)		(0x80000000 | x)
 -#endif
 +#include <uvm/uvm_extern.h>
  
 -#define	EFI_SUCCESS		0
 -#define	EFI_INVALID_PARAMETER	EFIERR(2)
 -#define	EFI_UNSUPPORTED		EFIERR(3)
 -#define	EFI_BUFFER_TOO_SMALL	EFIERR(5)
 -#define	EFI_DEVICE_ERROR	EFIERR(7)
 -#define	EFI_WRITE_PROTECTED	EFIERR(8)
 -#define	EFI_OUT_OF_RESOURCES	EFIERR(9)
 -#define	EFI_NOT_FOUND		EFIERR(14)
 -#define	EFI_SECURITY_VIOLATION	EFIERR(26)
 +#include <dev/efivar.h>
 +#include <dev/mm.h>
  
  #include "ioconf.h"
  
 @@ -149,6 +136,201 @@ efi_status_to_error(efi_status status)
  	}
  }
  
 +/* XXX move to efi.h */
 +#define	EFI_SYSTEM_RESOURCE_TABLE_GUID					      \
 +	{0xb122a263,0x3661,0x4f68,0x99,0x29,{0x78,0xf8,0xb0,0xd6,0x21,0x80}}
 +#define	EFI_PROPERTIES_TABLE						      \
 +	{0x880aaca3,0x4adc,0x4a04,0x90,0x79,{0xb7,0x47,0x34,0x08,0x25,0xe5}}
 +
 +#define	EFI_SYSTEM_RESOURCE_TABLE_FIRMWARE_RESOURCE_VERSION	1
 +
 +struct EFI_SYSTEM_RESOURCE_ENTRY {
 +	struct uuid	FwClass;
 +	uint32_t	FwType;
 +	uint32_t	FwVersion;
 +	uint32_t	LowestSupportedFwVersion;
 +	uint32_t	CapsuleFlags;
 +	uint32_t	LastAttemptVersion;
 +	uint32_t	LastAttemptStatus;
 +};
 +
 +struct EFI_SYSTEM_RESOURCE_TABLE {
 +	uint32_t	FwResourceCount;
 +	uint32_t	FwResourceCountMax;
 +	uint64_t	FwResourceVersion;
 +	struct EFI_SYSTEM_RESOURCE_ENTRY	Entries[];
 +};
 +
 +static void *
 +efi_map_pa(uint64_t addr, bool *directp)
 +{
 +	paddr_t pa = addr;
 +	vaddr_t va;
 +
 +	/*
 +	 * Verify the address is not truncated by conversion to
 +	 * paddr_t.  This might happen with a 64-bit EFI booting a
 +	 * 32-bit OS.
 +	 */
 +	if (pa != addr)
 +		return NULL;
 +
 +	/*
 +	 * Try direct-map if we have it.  If it works, note that it was
 +	 * direct-mapped for efi_unmap.
 +	 */
 +#ifdef __HAVE_MM_MD_DIRECT_MAPPED_PHYS
 +	if (mm_md_direct_mapped_phys(pa, &va)) {
 +		*directp = true;
 +		return (void *)va;
 +	}
 +#endif
 +
 +	/*
 +	 * No direct map.  Reserve a page of kernel virtual address
 +	 * space, with no backing, to map to the physical address.
 +	 */
 +	va = uvm_km_alloc(kernel_map, PAGE_SIZE, 0,
 +	    UVM_KMF_VAONLY|UVM_KMF_WAITVA);
 +	KASSERT(va != 0);
 +
 +	/*
 +	 * Map the kva page to the physical address and update the
 +	 * kernel pmap so we can use it.
 +	 */
 +	pmap_kenter_pa(va, pa, VM_PROT_READ, 0);
 +	pmap_update(pmap_kernel());
 +
 +	/*
 +	 * Success!  Return the VA and note that it was not
 +	 * direct-mapped for efi_unmap.
 +	 */
 +	*directp = false;
 +	return (void *)va;
 +}
 +
 +static void
 +efi_unmap(void *ptr, bool direct)
 +{
 +	vaddr_t va = (vaddr_t)ptr;
 +
 +	/*
 +	 * If it was direct-mapped, nothing to do here.
 +	 */
 +	if (direct)
 +		return;
 +
 +	/*
 +	 * First remove the mapping from the kernel pmap so that it can
 +	 * be reused, before we free the kva and let anyone else reuse
 +	 * it.
 +	 */
 +	pmap_kremove(va, PAGE_SIZE);
 +	pmap_update(pmap_kernel());
 +
 +	/*
 +	 * Next free the kva so it can be reused by someone else.
 +	 */
 +	uvm_km_free(kernel_map, va, PAGE_SIZE, UVM_KMF_VAONLY);
 +}
 +
 +static int
 +efi_ioctl_got_table(struct efi_get_table_ioc *ioc, void *ptr, size_t len)
 +{
 +
 +	/*
 +	 * Return the actual table length.
 +	 */
 +	ioc->table_len = len;
 +
 +	/*
 +	 * Copy out as much as we can into the user's allocated buffer.
 +	 */
 +	return copyout(ptr, ioc->buf, MIN(ioc->buf_len, len));
 +}
 +
 +static int
 +efi_ioctl_get_esrt(struct efi_get_table_ioc *ioc,
 +    struct EFI_SYSTEM_RESOURCE_TABLE *tab)
 +{
 +
 +	/*
 +	 * Verify the firmware resource version is one we understand.
 +	 */
 +	if (tab->FwResourceVersion !=
 +	    EFI_SYSTEM_RESOURCE_TABLE_FIRMWARE_RESOURCE_VERSION)
 +		return ENOENT;
 +
 +	/*
 +	 * Verify the resource count fits within the single page we
 +	 * have mapped.
 +	 *
 +	 * XXX What happens if it doesn't?  Are we expected to map more
 +	 * than one page, according to the table header?  The UEFI spec
 +	 * is unclear on this.
 +	 */
 +	const size_t entry_space = PAGE_SIZE -
 +	    offsetof(struct EFI_SYSTEM_RESOURCE_TABLE, Entries);
 +	if (tab->FwResourceCount > entry_space/sizeof(tab->Entries[0]))
 +		return ENOENT;
 +
 +	/*
 +	 * Success!  Return everything through the last table entry.
 +	 */
 +	const size_t len = offsetof(struct EFI_SYSTEM_RESOURCE_TABLE,
 +	    Entries[tab->FwResourceCount]);
 +	return efi_ioctl_got_table(ioc, tab, len);
 +}
 +
 +static int
 +efi_ioctl_get_table(struct efi_get_table_ioc *ioc)
 +{
 +	uint64_t addr;
 +	bool direct;
 +	efi_status status;
 +	int error;
 +
 +	/*
 +	 * If the platform doesn't support it yet, fail now.
 +	 */
 +	if (efi_ops->efi_gettab == NULL)
 +		return ENODEV;
 +
 +	/*
 +	 * Get the address of the requested table out of the EFI
 +	 * configuration table.
 +	 */
 +	status = efi_ops->efi_gettab(&ioc->uuid, &addr);
 +	if (status != EFI_SUCCESS)
 +		return efi_status_to_error(status);
 +
 +	/*
 +	 * UEFI provides no generic way to identify the size of the
 +	 * table, so we have to bake knowledge of every vendor GUID
 +	 * into this code to safely expose the right amount of data to
 +	 * userland.
 +	 *
 +	 * We even have to bake knowledge of which ones are physically
 +	 * addressed and which ones might be virtually addressed
 +	 * according to the vendor GUID into this code, although for
 +	 * the moment we never use RT->SetVirtualAddressMap so we only
 +	 * ever have to deal with physical addressing.
 +	 */
 +	if (memcmp(&ioc->uuid, &(struct uuid)EFI_SYSTEM_RESOURCE_TABLE_GUID,
 +		sizeof(ioc->uuid)) == 0) {
 +		struct EFI_SYSTEM_RESOURCE_TABLE *tab;
 +
 +		if ((tab = efi_map_pa(addr, &direct)) == NULL)
 +			return ENOENT;
 +		error = efi_ioctl_get_esrt(ioc, tab);
 +		efi_unmap(tab, direct);
 +	} else {
 +		error = ENOENT;
 +	}
 +
 +	return error;
 +}
 +
  static int
  efi_ioctl_var_get(struct efi_var_ioc *var)
  {
 @@ -289,6 +471,8 @@ efi_ioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l)
  	KASSERT(efi_ops != NULL);
  
  	switch (cmd) {
 +	case EFIIOC_GET_TABLE:
 +		return efi_ioctl_get_table(data);
  	case EFIIOC_VAR_GET:
  		return efi_ioctl_var_get(data);
  	case EFIIOC_VAR_NEXT:
 diff --git a/sys/dev/efi/efi.h b/sys/dev/efi/efi.h
 index 1f7fe910d06..544c81c0ca5 100644
 --- a/sys/dev/efi/efi.h
 +++ b/sys/dev/efi/efi.h
 @@ -44,6 +44,22 @@
  #define	EFIAPI	/* empty */
  #endif
  
 +#ifdef _LP64
 +#define	EFIERR(x)		(0x8000000000000000ul | (x))
 +#else
 +#define	EFIERR(x)		(0x80000000ul | (x))
 +#endif
 +
 +#define	EFI_SUCCESS		0
 +#define	EFI_INVALID_PARAMETER	EFIERR(2)
 +#define	EFI_UNSUPPORTED		EFIERR(3)
 +#define	EFI_BUFFER_TOO_SMALL	EFIERR(5)
 +#define	EFI_DEVICE_ERROR	EFIERR(7)
 +#define	EFI_WRITE_PROTECTED	EFIERR(8)
 +#define	EFI_OUT_OF_RESOURCES	EFIERR(9)
 +#define	EFI_NOT_FOUND		EFIERR(14)
 +#define	EFI_SECURITY_VIOLATION	EFIERR(26)
 +
  enum efi_reset {
  	EFI_RESET_COLD,
  	EFI_RESET_WARM,
 diff --git a/sys/dev/efivar.h b/sys/dev/efivar.h
 index 21d6d61fd26..72aeb8c6fba 100644
 --- a/sys/dev/efivar.h
 +++ b/sys/dev/efivar.h
 @@ -29,16 +29,20 @@
  #ifndef _DEV_EFIVAR_H
  #define _DEV_EFIVAR_H
  
 +#include <sys/uuid.h>
 +#include <sys/types.h>
 +
  #include <machine/efi.h>
  
  struct efi_ops {
  	efi_status	(*efi_gettime)(struct efi_tm *, struct efi_tmcap *);
  	efi_status	(*efi_settime)(struct efi_tm *);
  	efi_status	(*efi_getvar)(uint16_t *, struct uuid *, uint32_t *,
 -				      u_long *, void *);
 +			    u_long *, void *);
  	efi_status	(*efi_setvar)(uint16_t *, struct uuid *, uint32_t,
 -				      u_long, void *);
 +			    u_long, void *);
  	efi_status	(*efi_nextvar)(u_long *, uint16_t *, struct uuid *);
 +	efi_status	(*efi_gettab)(const struct uuid *, uint64_t *);
  };
  
  void	efi_register_ops(const struct efi_ops *);
 diff --git a/sys/sys/efiio.h b/sys/sys/efiio.h
 index 8f3a9a2d54e..c50c2c416fa 100644
 --- a/sys/sys/efiio.h
 +++ b/sys/sys/efiio.h
 @@ -48,6 +48,13 @@
  #define	EFI_VARIABLE_APPEND_WRITE				0x00000040
  #define	EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS		0x00000080
  
 +struct efi_get_table_ioc {
 +	void *		buf;
 +	struct uuid	uuid;
 +	size_t		table_len;
 +	size_t		buf_len;
 +};
 +
  struct efi_var_ioc {
  	uint16_t *	name;		/* vendor's variable name */
  	size_t		namesize;	/* size in bytes of the name buffer */
 @@ -57,6 +64,7 @@ struct efi_var_ioc {
  	size_t		datasize;	/* size in bytes of the data buffer */
  };
  
 +#define	EFIIOC_GET_TABLE	_IOWR('e', 1, struct efi_get_table_ioc)
  #define	EFIIOC_VAR_GET		_IOWR('e', 4, struct efi_var_ioc)
  #define	EFIIOC_VAR_NEXT		_IOWR('e', 5, struct efi_var_ioc)
  #define	EFIIOC_VAR_SET		_IOWR('e', 7, struct efi_var_ioc)
 
 --e94Sh53BmyHhQjjd
 Content-Type: text/plain; charset=us-ascii
 Content-Disposition: attachment; filename="efitable.c"
 
 /*-
  * Copyright (c) 2022 3mdeb <contact%3mdeb.com@localhost>
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
  * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include <sys/cdefs.h>
 
 #include <sys/types.h>
 #include <sys/efiio.h>
 #include <sys/ioctl.h>
 #include <sys/param.h>
 #include <sys/stat.h>
 #include <err.h>
 #include <fcntl.h>
 #include <getopt.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sysexits.h>
 #include <unistd.h>
 #include <uuid.h>
 #include <errno.h>
 
 #define TABLE_MAX_LEN 30
 
 #ifdef EFI_TABLE_ESRT
 #undef EFI_TABLE_ESRT
 #endif
 
 #define nitems(_a)      (sizeof((_a)) / sizeof((_a)[0]))
 
 #define	EFI_TABLE_ESRT					\
 			{0xb122a263,0x3661,0x4f68,0x99,0x29,{0x78,0xf8,0xb0,0xd6,0x21,0x80}}
 
 static void efi_table_print_esrt(void *data);
 static void usage(void);
 static void fail(const char msg[]);
 
 struct efi_table_op {
 	char name[TABLE_MAX_LEN];
 	void (*parse) (void *);
 	struct uuid uuid;
 };
 
 static const struct efi_table_op efi_table_ops[] = {
 	{ .name = "esrt",
 	  .parse = efi_table_print_esrt,
 	  .uuid = EFI_TABLE_ESRT },
 };
 
 struct efi_esrt_table {
 	uint32_t	fw_resource_count;
 	uint32_t	fw_resource_count_max;
 	uint64_t	fw_resource_version;
 	uint8_t	entries[];
 };
 
 struct efi_esrt_entry_v1 {
 	struct uuid	fw_class;
 	uint32_t 	fw_type;
 	uint32_t	fw_version;
 	uint32_t	lowest_supported_fw_version;
 	uint32_t	capsule_flags;
 	uint32_t	last_attempt_version;
 	uint32_t	last_attempt_status;
 };
 
 int
 main(int argc, char **argv)
 {
 	struct efi_get_table_ioc table = {
 		.buf = NULL,
 		.buf_len = 0,
 		.table_len = 0
 	};
 	int efi_fd, ch, rc = 1, efi_idx = -1;
 	bool got_table = false;
 	bool table_set = false;
 	bool uuid_set = false;
 	struct option longopts[] = {
 		{ "uuid",  required_argument, NULL, 'u' },
 		{ "table", required_argument, NULL, 't' },
 		{ NULL,    0,                 NULL,  0  }
 	};
 
 	while ((ch = getopt_long(argc, argv, "u:t:", longopts, NULL)) != -1) {
 		switch (ch) {
 			case 'u': {
 					  char *uuid_str = optarg;
 					  struct uuid uuid;
 					  uint32_t status;
 					  size_t n;
 
 					  uuid_set = 1;
 
 					  uuid_from_string(uuid_str, &uuid, &status);
 					  if (status != uuid_s_ok)
 						  fail("invalid UUID");
 
 					  for (n = 0; n < nitems(efi_table_ops); n++) {
 						  if (!memcmp(&uuid,
 									  &efi_table_ops[n].uuid,
 									  sizeof(uuid))) {
 							  efi_idx = n;
 							  got_table = true;
 							  break;
 						  }
 					  }
 					  break;
 				  }
 			case 't': {
 					  char *table_name = optarg;
 					  size_t n;
 
 					  table_set = true;
 
 					  for (n = 0; n < nitems(efi_table_ops); n++) {
 						  if (!strcmp(table_name,
 									  efi_table_ops[n].name)) {
 							  efi_idx = n;
 							  got_table = true;
 							  break;
 						  }
 					  }
 
 					  if (!got_table)
 						  fail("unsupported efi table");
 					  break;
 				  }
 			default:
 				  usage();
 		}
 	}
 
 	if (!table_set && !uuid_set)
 		fail("table is not set");
 
 	if (!got_table)
 		fail("unsupported table");
 
 	efi_fd = open("/dev/efi", O_RDWR);
 	if (efi_fd < 0)
 		fail("/dev/efi");
 
 	table.uuid = efi_table_ops[efi_idx].uuid;
 	if (ioctl(efi_fd, EFIIOC_GET_TABLE, &table) == -1)
 		fail(NULL);
 
 	table.buf = malloc(table.table_len);
 	table.buf_len = table.table_len;
 
 	if (ioctl(efi_fd, EFIIOC_GET_TABLE, &table) == -1)
 		fail(NULL);
 	close(efi_fd);
 
 	efi_table_ops[efi_idx].parse(table.buf);
 
 	return (rc);
 }
 
 static void
 efi_table_print_esrt(void *data)
 {
 	struct efi_esrt_table *esrt = data;
 	struct efi_esrt_entry_v1 *esre = (void *)esrt->entries;
 	printf("ESRT FwResourceCount = %d\n", esrt->fw_resource_count);
 
 	uint32_t i;
 	for (i = 0; i < esrt->fw_resource_count; i++) {
 		char *uuid = NULL;
 		uuid_to_string(&esre[i].fw_class, &uuid, NULL);
 
 		printf("ESRT[%d]:\n", i);
 		printf("  FwClass: %s\n", uuid);
 		printf("  FwType: 0x%08x\n", esre[i].fw_type);
 		printf("  FwVersion: 0x%08x\n", esre[i].fw_version);
 		printf("  LowestSupportedFwVersion: 0x%08x\n",
 				esre[i].lowest_supported_fw_version);
 		printf("  CapsuleFlags: 0x%08x\n",
 				esre[i].capsule_flags);
 		printf("  LastAttemptVersion: 0x%08x\n",
 				esre[i].last_attempt_version);
 		printf("  LastAttemptStatus: 0x%08x\n",
 				esre[i].last_attempt_status);
 
 		free(uuid);
 	}
 }
 
 static void usage(void)
 {
 	printf("usage: efitable [-u uuid | -t name]\n");
 	exit(1);
 }
 
 static void fail(const char msg[])
 {
 	if (msg != NULL)
 		fprintf(stderr, "%s\n", msg);
 	else
 		fprintf(stderr, "errno: %s\n", strerror(errno));
 	exit(1);
 }
 
 --e94Sh53BmyHhQjjd--
 


Home | Main Index | Thread Index | Old Index