1/* $NetBSD: apei_mapreg.c,v 1.4 2024/03/22 20:47:52 riastradh Exp $ */ 2 3/*- 4 * Copyright (c) 2024 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29/* 30 * Pre-mapped ACPI register access 31 * 32 * XXX This isn't APEI-specific -- it should be moved into the general 33 * ACPI API, and unified with the AcpiRead/AcpiWrite implementation. 34 */ 35 36#include <sys/cdefs.h> 37__KERNEL_RCSID(0, "$NetBSD: apei_mapreg.c,v 1.4 2024/03/22 20:47:52 riastradh Exp $"); 38 39#include <sys/types.h> 40 41#include <sys/atomic.h> 42 43#include <dev/acpi/acpivar.h> 44#include <dev/acpi/apei_mapreg.h> 45 46/* 47 * apei_mapreg_map(reg) 48 * 49 * Return a mapping for use with apei_mapreg_read, or NULL if it 50 * can't be mapped. 51 */ 52struct apei_mapreg * 53apei_mapreg_map(const ACPI_GENERIC_ADDRESS *reg) 54{ 55 56 /* 57 * Verify the result is reasonable. 58 */ 59 switch (reg->BitWidth) { 60 case 8: 61 case 16: 62 case 32: 63 case 64: 64 break; 65 default: 66 return NULL; 67 } 68 69 /* 70 * Verify we know how to do the access width. 71 */ 72 switch (reg->AccessWidth) { 73 case 1: /* 8-bit */ 74 case 2: /* 16-bit */ 75 case 3: /* 32-bit */ 76 break; 77 case 4: /* 64-bit */ 78 if (reg->SpaceId == ACPI_ADR_SPACE_SYSTEM_IO) 79 return NULL; 80 break; 81 default: 82 return NULL; 83 } 84 85 /* 86 * Verify we don't need to shift anything, because I can't 87 * figure out how the shifting is supposed to work in five 88 * minutes of looking at the spec. 89 */ 90 switch (reg->BitOffset) { 91 case 0: 92 break; 93 default: 94 return NULL; 95 } 96 97 /* 98 * Verify the bit width is a multiple of the access width so 99 * we're not accessing more than we need. 100 */ 101 if (reg->BitWidth % (8*(1 << (reg->AccessWidth - 1)))) 102 return NULL; 103 104 /* 105 * Dispatch on the space id. 106 */ 107 switch (reg->SpaceId) { 108 case ACPI_ADR_SPACE_SYSTEM_IO: 109 /* 110 * Just need to return something non-null -- no state 111 * to record for a mapping. 112 */ 113 return (struct apei_mapreg *)__UNCONST(reg); 114 case ACPI_ADR_SPACE_SYSTEM_MEMORY: 115 return AcpiOsMapMemory(reg->Address, 116 1 << (reg->AccessWidth - 1)); 117 default: 118 return NULL; 119 } 120} 121 122/* 123 * apei_mapreg_unmap(reg, map) 124 * 125 * Unmap a mapping previously returned by apei_mapreg_map. 126 */ 127void 128apei_mapreg_unmap(const ACPI_GENERIC_ADDRESS *reg, 129 struct apei_mapreg *map) 130{ 131 132 switch (reg->SpaceId) { 133 case ACPI_ADR_SPACE_SYSTEM_IO: 134 KASSERT(map == __UNCONST(reg)); 135 break; 136 case ACPI_ADR_SPACE_SYSTEM_MEMORY: 137 AcpiOsUnmapMemory(map, 1 << (reg->AccessWidth - 1)); 138 break; 139 default: 140 panic("invalid register mapping"); 141 } 142} 143 144/* 145 * apei_mapreg_read(reg, map) 146 * 147 * Read from reg via map previously obtained by apei_mapreg_map. 148 */ 149uint64_t 150apei_mapreg_read(const ACPI_GENERIC_ADDRESS *reg, 151 const struct apei_mapreg *map) 152{ 153 unsigned chunkbits = NBBY*(1 << (reg->AccessWidth - 1)); 154 unsigned i, n = reg->BitWidth / chunkbits; 155 uint64_t v = 0; 156 157 for (i = 0; i < n; i++) { 158 uint64_t chunk; 159 160 switch (reg->SpaceId) { 161 case ACPI_ADR_SPACE_SYSTEM_IO: { 162 ACPI_IO_ADDRESS addr; 163 uint32_t chunk32 = 0; 164 ACPI_STATUS rv; 165 166 switch (reg->AccessWidth) { 167 case 1: 168 addr = reg->Address + i; 169 break; 170 case 2: 171 addr = reg->Address + 2*i; 172 break; 173 case 3: 174 addr = reg->Address + 4*i; 175 break; 176 case 4: /* no 64-bit I/O ports */ 177 default: 178 __unreachable(); 179 } 180 rv = AcpiOsReadPort(addr, &chunk32, 181 NBBY*(1 << (reg->AccessWidth - 1))); 182 KASSERTMSG(!ACPI_FAILURE(rv), "%s", 183 AcpiFormatException(rv)); 184 chunk = chunk32; 185 break; 186 } 187 case ACPI_ADR_SPACE_SYSTEM_MEMORY: 188 switch (reg->AccessWidth) { 189 case 1: 190 chunk = *((volatile const uint8_t *)map + i); 191 break; 192 case 2: 193 chunk = *((volatile const uint16_t *)map + i); 194 break; 195 case 3: 196 chunk = *((volatile const uint32_t *)map + i); 197 break; 198 case 4: 199 chunk = *((volatile const uint64_t *)map + i); 200 break; 201 default: 202 __unreachable(); 203 } 204 break; 205 default: 206 __unreachable(); 207 } 208 v |= chunk << (i*chunkbits); 209 } 210 211 membar_acquire(); /* XXX probably not right for MMIO */ 212 return v; 213} 214 215/* 216 * apei_mapreg_write(reg, map, v) 217 * 218 * Write to reg via map previously obtained by apei_mapreg_map. 219 */ 220void 221apei_mapreg_write(const ACPI_GENERIC_ADDRESS *reg, struct apei_mapreg *map, 222 uint64_t v) 223{ 224 unsigned chunkbits = NBBY*(1 << (reg->AccessWidth - 1)); 225 unsigned i, n = reg->BitWidth / chunkbits; 226 227 membar_release(); /* XXX probably not right for MMIO */ 228 for (i = 0; i < n; i++) { 229 uint64_t chunk = v >> (i*chunkbits); 230 231 switch (reg->SpaceId) { 232 case ACPI_ADR_SPACE_SYSTEM_IO: { 233 ACPI_IO_ADDRESS addr; 234 ACPI_STATUS rv; 235 236 switch (reg->AccessWidth) { 237 case 1: 238 addr = reg->Address + i; 239 break; 240 case 2: 241 addr = reg->Address + 2*i; 242 break; 243 case 3: 244 addr = reg->Address + 4*i; 245 break; 246 case 4: /* no 64-bit I/O ports */ 247 default: 248 __unreachable(); 249 } 250 rv = AcpiOsWritePort(addr, chunk, 251 NBBY*(1 << (reg->AccessWidth - 1))); 252 KASSERTMSG(!ACPI_FAILURE(rv), "%s", 253 AcpiFormatException(rv)); 254 break; 255 } 256 case ACPI_ADR_SPACE_SYSTEM_MEMORY: 257 switch (reg->AccessWidth) { 258 case 1: 259 *((volatile uint8_t *)map + i) = chunk; 260 break; 261 case 2: 262 *((volatile uint16_t *)map + i) = chunk; 263 break; 264 case 3: 265 *((volatile uint32_t *)map + i) = chunk; 266 break; 267 case 4: 268 *((volatile uint64_t *)map + i) = chunk; 269 break; 270 default: 271 __unreachable(); 272 } 273 break; 274 default: 275 __unreachable(); 276 } 277 } 278} 279