summaryrefslogtreecommitdiff
path: root/sys/arch
diff options
context:
space:
mode:
authorAriane van der Steldt <ariane@cvs.openbsd.org>2009-04-20 13:26:21 +0000
committerAriane van der Steldt <ariane@cvs.openbsd.org>2009-04-20 13:26:21 +0000
commit766a3b0775f071fc90f4d5596ea776418758a129 (patch)
treeaa233740981712afd1331ee174926dea4353b911 /sys/arch
parenteb6f96e5e437015a36d400dab7888625c8044243 (diff)
amas device for amd64, describes the physical memory layout on AMD64 CPU.
Replaces pchb with amas for the AMD64 address map. amas0 at pci0 dev 24 function 1 "AMD AMD64 0Fh Address Map" rev 0x00 Currently disabled (causing pchb to attach instead). ok art@
Diffstat (limited to 'sys/arch')
-rw-r--r--sys/arch/amd64/conf/GENERIC3
-rw-r--r--sys/arch/amd64/conf/files.amd647
-rw-r--r--sys/arch/amd64/include/amas.h82
-rw-r--r--sys/arch/amd64/pci/amas.c284
4 files changed, 374 insertions, 2 deletions
diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC
index dfc67ab23a5..b883278c365 100644
--- a/sys/arch/amd64/conf/GENERIC
+++ b/sys/arch/amd64/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.261 2009/04/19 21:33:49 mk Exp $
+# $OpenBSD: GENERIC,v 1.262 2009/04/20 13:26:20 ariane Exp $
#
# For further information on compiling OpenBSD kernels, see the config(8)
# man page.
@@ -68,6 +68,7 @@ pcib* at pci? # PCI-ISA bridge
amdpcib* at pci? # AMD 8111 LPC bridge
kate* at pci? # AMD K8 temperature sensor
km* at pci? # AMD K10 temperature sensor
+amas* at pci? disable # AMD memory configuration
# National Semiconductor LM7[89] and compatible hardware monitors
lm0 at isa? port 0x290
diff --git a/sys/arch/amd64/conf/files.amd64 b/sys/arch/amd64/conf/files.amd64
index 731fa2ca72c..0c7522be0a7 100644
--- a/sys/arch/amd64/conf/files.amd64
+++ b/sys/arch/amd64/conf/files.amd64
@@ -1,4 +1,4 @@
-# $OpenBSD: files.amd64,v 1.44 2009/02/15 02:03:40 marco Exp $
+# $OpenBSD: files.amd64,v 1.45 2009/04/20 13:26:20 ariane Exp $
maxpartitions 16
maxusers 2 16 128
@@ -112,6 +112,11 @@ device pchb: pcibus, agpbus
attach pchb at pci
file arch/amd64/pci/pchb.c pchb
+# AMAS AMD memory address switch
+device amas
+attach amas at pci
+file arch/amd64/pci/amas.c amas
+
# AGP bridge support. most attach at pchb
include "dev/pci/files.agp"
file arch/amd64/pci/agp_machdep.c agp
diff --git a/sys/arch/amd64/include/amas.h b/sys/arch/amd64/include/amas.h
new file mode 100644
index 00000000000..ab5b90b942e
--- /dev/null
+++ b/sys/arch/amd64/include/amas.h
@@ -0,0 +1,82 @@
+/* $OpenBSD: amas.h,v 1.1 2009/04/20 13:26:20 ariane Exp $ */
+
+/*
+ * Copyright (c) 2009 Ariane van der Steldt <ariane@stack.nl>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Device: amas (AMD memory access/address switch).
+ *
+ * Driver for the amd athlon/opteron 64 address map.
+ * This device is integrated in 64-bit Athlon and Opteron cpus
+ * and contains mappings for memory to processor nodes.
+ *
+ * The AMD cpu can access memory in two ways: interleaved and non-interleaved.
+ *
+ * In interleaved mode, 16 MB regions are rotated across each node,
+ * example for 2 nodes:
+ * - region 0 (0-16 MB) on node 0.
+ * - region 1 (16-32 MB) on node 1.
+ * - region 2 (33-48 MB) on node 0.
+ * - region 3 (48-64 MB) on node 1.
+ * ...
+ * - region 2 * N on node 0.
+ * - region 2 * N + 1 on node 1.
+ * Interleaved mode requires that each node has the same amount of physical
+ * memory.
+ *
+ * In non-interleaved mode, memory for each node is a continuous address space,
+ * example for 2 nodes:
+ * - region 0 (all memory on CPU package 0) on node 0.
+ * - region 1 (all memory on CPU package 1) on node 1.
+ * Non-interleaved mode requires either that each node has the same amount of
+ * physical memory or that memory holes are allowed between each node.
+ *
+ * Configuring memory for interleaved or non-interleaved mode is handled by
+ * the BIOS.
+ */
+
+#ifndef _MACHINE_AMAS_H_
+#define _MACHINE_AMAS_H_
+
+#include <sys/types.h>
+#include <sys/device.h>
+
+#include <dev/pci/pcivar.h>
+
+#ifdef _KERNEL
+
+#define AMAS_MAX_NODES (8) /* AMAS supports 8 nodes at maximum. */
+
+/* Device configuration. */
+struct amas_softc {
+ struct device sc_dev;
+ /* PCI location of this device. */
+ pcitag_t pa_tag;
+ pci_chipset_tag_t pa_pc;
+ int family;
+};
+
+/* AMAS driver. */
+extern struct cfdriver amas_cd;
+
+int amas_match(struct device*, void*, void*);
+void amas_attach(struct device*, struct device*, void*);
+
+int amas_intl_nodes(struct amas_softc*);
+void amas_get_pagerange(struct amas_softc*, int node, paddr_t*, paddr_t*);
+
+#endif /* _KERNEL */
+#endif /* _MACHINE_AMAS_H_ */
diff --git a/sys/arch/amd64/pci/amas.c b/sys/arch/amd64/pci/amas.c
new file mode 100644
index 00000000000..6e1d05fdf33
--- /dev/null
+++ b/sys/arch/amd64/pci/amas.c
@@ -0,0 +1,284 @@
+/* $OpenBSD: amas.c,v 1.1 2009/04/20 13:26:20 ariane Exp $ */
+
+/*
+ * Copyright (c) 2009 Ariane van der Steldt <ariane@stack.nl>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Device: amas (AMD memory access/address switch).
+ *
+ * Driver for the amd athlon/opteron 64 address map.
+ * This device is integrated in 64-bit Athlon and Opteron cpus
+ * and contains mappings for memory to processor nodes.
+ */
+
+#include <machine/amas.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcidevs.h>
+
+int amas_match(struct device*, void*, void*);
+void amas_attach(struct device*, struct device*, void*);
+
+/*
+ * Amas device layout:
+ *
+ * - base/limit registers (on 0x0f, 0x10, 0x11)
+ * - extended base/limit registers (on 0x10)
+ *
+ * 0x0f, 0x10 support up to 8 nodes
+ * 0x11 supports up to 1 nodes
+ *
+ * base/limit registers use bits [31..16] to indicate address [39..24]
+ * extended base/limit registers use bits [7..0] to indicate address [47..40]
+ * base/limit addresses need to be shifted <<24 for memory address
+ * extended base/limit addresses need to be shifted <<40 for memory address
+ */
+
+#define AMAS_REG_BASE(node) (0x0040 + 0x08 * (node))
+#define AMAS_REG_LIMIT(node) (0x0044 + 0x08 * (node))
+#define AMAS_REG_EXTBASE(node) (0x0140 + 0x08 * (node))
+#define AMAS_REG_EXTLIMIT(node) (0x0144 + 0x08 * (node))
+
+#define AMAS_REG_BL_ADDR(reg) (((reg) >> 16) & 0xffff)
+#define AMAS_REG_EBL_ADDR(ereg) ((ereg) & 0xff)
+
+#define AMAS_REG_BL_SHIFT (24)
+#define AMAS_REG_EBL_SHIFT (40)
+
+#define AMAS_REG_BL_PGSHIFT (AMAS_REG_BL_SHIFT - PAGE_SHIFT)
+#define AMAS_REG_EBL_PGSHIFT (AMAS_REG_EBL_SHIFT - PAGE_SHIFT)
+
+/*
+ * Convert an address in amas to a page number.
+ *
+ * The device uses an inclusive mapping, where the upper bound address
+ * must be all 1's after shifting.
+ * The device driver uses C-style array indices, hence the +1 in the _LIMIT
+ * macro.
+ */
+#define AMAS_ADDR2PAGE_BASE(base, ebase) \
+ (((base) << AMAS_REG_BL_PGSHIFT) | ((ebase) << AMAS_REG_EBL_PGSHIFT))
+#define AMAS_ADDR2PAGE_LIMIT(base, ebase) \
+ (((base + 1) << AMAS_REG_BL_PGSHIFT) | ((ebase) << AMAS_REG_EBL_PGSHIFT))
+
+/*
+ * Node and interleave description.
+ * - base contains node selection [10..8] (on 0x0f, 0x10)
+ * - limit contains node selection bitmask [10..8] (on 0x0f, 0x10)
+ * - limit contains destination node [2..0] (on 0x0f, 0x10)
+ */
+#define AMAS_DST_NODE(base, limit) ((limit) & 0x07)
+#define AMAS_INTL_ENABLE(base, limit) (((base) >> 8) & 0x07)
+#define AMAS_INTL_SELECTOR(base, limit) (((limit) >> 8) & 0x07)
+
+/*
+ * Defines for family.
+ * Corresponds to the amas_feature[] constant below.
+ */
+#define AMAS_FAM_0Fh (0)
+#define AMAS_FAM_10h (1)
+#define AMAS_FAM_11h (2)
+
+/*
+ * Feature tests.
+ *
+ * 0x11 supports at max 1 node, 0x0f and 0x10 support up to 8 nodes.
+ * 0x11 has extended address registers.
+ * 0x0f, 0x10 can interleave memory.
+ */
+struct amas_feature_t {
+ int maxnodes;
+ int can_intl;
+ int has_extended_bl;
+};
+static const struct amas_feature_t amas_feature[] = {
+ /* Family 0x0f */
+ { 8, 1, 0 },
+ /* Family 0x10 */
+ { 8, 1, 1 },
+ /* Family 0x11 */
+ { 1, 0, 0 },
+};
+
+/* Probe code. */
+struct cfattach amas_ca = {
+ sizeof(struct amas_softc),
+ amas_match,
+ amas_attach
+};
+
+struct cfdriver amas_cd = {
+ NULL,
+ "amas",
+ DV_DULL
+};
+
+const struct pci_matchid amas_devices[] = {
+ { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_AMD64_0F_ADDR },
+ { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_AMD64_10_ADDR },
+ { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_AMD64_11_ADDR },
+};
+
+int
+amas_match(struct device* parent, void* match, void* aux)
+{
+ struct pci_attach_args* pa = aux;
+
+ if (pci_matchbyid(pa, amas_devices, nitems(amas_devices)))
+ return 2; /* override pchb */
+ return 0;
+}
+
+void
+amas_attach(struct device* parent, struct device* self, void* aux)
+{
+ struct pci_attach_args* pa = aux;
+ struct amas_softc* amas = (struct amas_softc*)self;
+#ifdef DEBUG
+ paddr_t start_pg, end_pg;
+ int nodes, i;
+#endif /* DEBUG */
+
+ amas->pa_tag = pa->pa_tag;
+ amas->pa_pc = pa->pa_pc;
+
+ switch (PCI_PRODUCT(pa->pa_id)) {
+ case PCI_PRODUCT_AMD_AMD64_0F_ADDR:
+ amas->family = AMAS_FAM_0Fh;
+ break;
+ case PCI_PRODUCT_AMD_AMD64_10_ADDR:
+ amas->family = AMAS_FAM_10h;
+ break;
+ case PCI_PRODUCT_AMD_AMD64_11_ADDR:
+ amas->family = AMAS_FAM_11h;
+ break;
+ }
+
+#ifdef DEBUG
+ nodes = amas_intl_nodes(amas);
+
+ printf(":");
+ if (nodes != 0) {
+ printf(" interleaved across %d nodes", nodes);
+ } else {
+ for (i = 0; i < AMAS_MAX_NODES; i++) {
+ amas_get_pagerange(amas, i, &start_pg, &end_pg);
+
+ if (start_pg == 0 && end_pg == 0)
+ printf(" []");
+ else
+ printf(" [%p, %p]", start_pg, end_pg);
+ }
+ }
+#endif /* DEBUG */
+ printf("\n");
+
+ return;
+}
+
+/*
+ * Returns the number of nodes across which the memory is interleaved.
+ * Returns 0 if the memory is not interleaved.
+ */
+int
+amas_intl_nodes(struct amas_softc* amas)
+{
+ pcireg_t base_reg, limit_reg;
+ int mask;
+
+ if (!amas_feature[amas->family].can_intl)
+ return 0;
+
+ /*
+ * Use node 0 on amas device to find interleave information.
+ * Node 0 is always present.
+ */
+
+ base_reg = pci_conf_read(amas->pa_pc, amas->pa_tag, AMAS_REG_BASE(0));
+ limit_reg = pci_conf_read(amas->pa_pc, amas->pa_tag, AMAS_REG_LIMIT(0));
+ mask = AMAS_INTL_ENABLE(base_reg, limit_reg);
+
+ return mask == 0 ? 0 : mask + 1;
+}
+
+/*
+ * Returns the range of memory that is contained on the given node.
+ * If the memory is interleaved, the result is undefined.
+ *
+ * The range is written in {start,end}_pg_idx.
+ * Note that these are page numbers and that these use array indices:
+ * pages are in this range if start <= pg_no < end.
+ *
+ * This device supports at most 8 nodes.
+ */
+void
+amas_get_pagerange(struct amas_softc* amas, int node,
+ paddr_t* start_pg_idx, paddr_t* end_pg_idx)
+{
+ pcireg_t base, ebase, limit, elimit;
+ paddr_t base_addr, ebase_addr, limit_addr, elimit_addr;
+
+ /* Sanity check: max AMAS_MAX_NODES supported. */
+ KASSERT(node >= 0 && node < AMAS_MAX_NODES);
+
+ if (node >= amas_feature[amas->family].maxnodes) {
+ /* Unsupported node: bail out early. */
+ *start_pg_idx = 0;
+ *end_pg_idx = 0;
+ return;
+ }
+
+ base = pci_conf_read(amas->pa_pc, amas->pa_tag,
+ AMAS_REG_BASE(node));
+ limit = pci_conf_read(amas->pa_pc, amas->pa_tag,
+ AMAS_REG_LIMIT(node));
+ base_addr = AMAS_REG_BL_ADDR(base);
+ limit_addr = AMAS_REG_BL_ADDR(limit);
+
+ ebase = 0;
+ elimit = 0;
+ ebase_addr = 0;
+ elimit_addr = 0;
+#if 0 /* Needs extended pci registers. */
+ if (amas_feature[amas->family].has_extended_bl) {
+ ebase = pci_conf_read(amas->pa_pc, amas->pa_tag,
+ AMAS_REG_EXTBASE(node));
+ elimit = pci_conf_read(amas->pa_pc, amas->pa_tag,
+ AMAS_REG_EXTLIMIT(node));
+ ebase_addr = AMAS_REG_EBL_ADDR(ebase);
+ elimit_addr = AMAS_REG_EBL_ADDR(elimit);
+ }
+#endif /* 0 */
+
+ if (base_addr == limit_addr && ebase_addr == elimit_addr) {
+ /* no memory present */
+ *start_pg_idx = 0;
+ *end_pg_idx = 0;
+ return;
+ }
+
+ /* Guaranteed by spec. */
+ KASSERT(node == AMAS_DST_NODE(base, limit));
+
+ *start_pg_idx = AMAS_ADDR2PAGE_BASE(base_addr, ebase_addr);
+ *end_pg_idx = AMAS_ADDR2PAGE_LIMIT(limit_addr, elimit_addr);
+ return;
+}