1 | /* $NetBSD: linux_writecomb.c,v 1.4 2015/10/17 21:06:42 jmcneill Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2013 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Taylor R. Campbell. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | #include <sys/cdefs.h> |
33 | __KERNEL_RCSID(0, "$NetBSD: linux_writecomb.c,v 1.4 2015/10/17 21:06:42 jmcneill Exp $" ); |
34 | |
35 | #if defined(__i386__) || defined(__x86_64__) |
36 | #define HAS_MTRR 1 |
37 | #endif |
38 | |
39 | #if defined(_KERNEL_OPT) && defined(HAS_MTRR) |
40 | #include "opt_mtrr.h" |
41 | #endif |
42 | |
43 | #include <sys/kmem.h> |
44 | #include <sys/mutex.h> |
45 | |
46 | #if defined(MTRR) |
47 | #include <machine/mtrr.h> |
48 | #endif |
49 | |
50 | #include <linux/idr.h> |
51 | #include <linux/io.h> |
52 | |
53 | static struct { |
54 | kmutex_t lock; |
55 | struct idr idr; |
56 | } linux_writecomb __cacheline_aligned; |
57 | |
58 | int |
59 | linux_writecomb_init(void) |
60 | { |
61 | |
62 | mutex_init(&linux_writecomb.lock, MUTEX_DEFAULT, IPL_VM); |
63 | idr_init(&linux_writecomb.idr); |
64 | |
65 | return 0; |
66 | } |
67 | |
68 | void |
69 | linux_writecomb_fini(void) |
70 | { |
71 | |
72 | KASSERT(idr_is_empty(&linux_writecomb.idr)); |
73 | idr_destroy(&linux_writecomb.idr); |
74 | mutex_destroy(&linux_writecomb.lock); |
75 | } |
76 | |
77 | int |
78 | arch_phys_wc_add(unsigned long base, unsigned long size) |
79 | { |
80 | #if defined(MTRR) |
81 | struct mtrr *mtrr; |
82 | int n = 1; |
83 | int id; |
84 | int ret; |
85 | |
86 | mtrr = kmem_alloc(sizeof(*mtrr), KM_SLEEP); |
87 | mtrr->base = base; |
88 | mtrr->len = size; |
89 | mtrr->type = MTRR_TYPE_WC; |
90 | mtrr->flags = MTRR_VALID; |
91 | |
92 | /* XXX errno NetBSD->Linux */ |
93 | ret = -mtrr_set(mtrr, &n, NULL, MTRR_GETSET_KERNEL); |
94 | if (ret) { |
95 | KASSERT(n == 0); |
96 | goto fail0; |
97 | } |
98 | KASSERT(n == 1); |
99 | |
100 | idr_preload(GFP_KERNEL); |
101 | mutex_spin_enter(&linux_writecomb.lock); |
102 | id = idr_alloc(&linux_writecomb.idr, mtrr, 0, 0, GFP_NOWAIT); |
103 | mutex_spin_exit(&linux_writecomb.lock); |
104 | idr_preload_end(); |
105 | if (id < 0) |
106 | goto fail1; |
107 | |
108 | return id; |
109 | |
110 | fail1: KASSERT(id < 0); |
111 | mtrr->type = 0; |
112 | mtrr->flags = 0; |
113 | /* XXX errno NetBSD->Linux */ |
114 | ret = -mtrr_set(mtrr, &n, NULL, MTRR_GETSET_KERNEL); |
115 | KASSERT(ret == 0); |
116 | KASSERT(n == 1); |
117 | ret = id; |
118 | fail0: KASSERT(ret < 0); |
119 | kmem_free(mtrr, sizeof(*mtrr)); |
120 | return ret; |
121 | #else |
122 | return -1; |
123 | #endif |
124 | } |
125 | |
126 | void |
127 | arch_phys_wc_del(int id) |
128 | { |
129 | #if defined(MTRR) |
130 | struct mtrr *mtrr; |
131 | int n; |
132 | int ret __diagused; |
133 | |
134 | KASSERT(0 <= id); |
135 | |
136 | mutex_spin_enter(&linux_writecomb.lock); |
137 | mtrr = idr_find(&linux_writecomb.idr, id); |
138 | idr_remove(&linux_writecomb.idr, id); |
139 | mutex_spin_enter(&linux_writecomb.lock); |
140 | |
141 | if (mtrr != NULL) { |
142 | mtrr->type = 0; |
143 | mtrr->flags = 0; |
144 | /* XXX errno NetBSD->Linux */ |
145 | ret = -mtrr_set(mtrr, &n, NULL, MTRR_GETSET_KERNEL); |
146 | KASSERT(ret == 0); |
147 | KASSERT(n == 1); |
148 | kmem_free(mtrr, sizeof(*mtrr)); |
149 | } |
150 | #endif |
151 | } |
152 | |
153 | int |
154 | phys_wc_to_mtrr_index(int handle) |
155 | { |
156 | |
157 | /* XXX Actually implement this...requires changes to our MTRR API. */ |
158 | return handle; |
159 | } |
160 | |