1/*-
2 * ----------------------------------------------------------------------------
3 * "THE BEER-WARE LICENSE" (Revision 42):
4 * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
5 * can do whatever you want with this stuff. If we meet some day, and you think
6 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7 * ----------------------------------------------------------------------------
8 *
9 * $FreeBSD$
10 *
11 */
12
13#include "opt_md.h"
14#include "opt_ski.h"
15
16#include <sys/param.h>
17#include <sys/systm.h>
18#include <sys/bio.h>
19#include <sys/conf.h>
20#include <sys/kernel.h>
21#include <sys/linker.h>
22#include <sys/lock.h>
23#include <sys/malloc.h>
24#include <sys/mutex.h>
25#include <sys/queue.h>
26#include <sys/sysctl.h>
27#include <sys/proc.h>
28#include <vm/vm.h>
29#include <vm/vm_kern.h>
30#include <vm/vm_page.h>
31#include <vm/vm_map.h>
32#include <vm/vm_extern.h>
33#include <vm/vm_object.h>
34#include <vm/vm_pager.h>
35#include <machine/md_var.h>
36#include <geom/geom_disk.h>
37
38#ifndef SKI_ROOT_FILESYSTEM
39#define SKI_ROOT_FILESYSTEM	"ia64-root.fs"
40#endif
41
42#define SSC_OPEN			50
43#define SSC_CLOSE			51
44#define SSC_READ			52
45#define SSC_WRITE			53
46#define SSC_GET_COMPLETION		54
47#define SSC_WAIT_COMPLETION		55
48
49struct disk_req {
50	unsigned long addr;
51	unsigned len;
52};
53
54struct disk_stat {
55	int fd;
56	unsigned count;
57};
58
59static u_int64_t
60ssc(u_int64_t in0, u_int64_t in1, u_int64_t in2, u_int64_t in3, int which)
61{
62	register u_int64_t ret0 __asm("r8");
63
64	__asm __volatile("mov r15=%1\n\t"
65			 "break 0x80001"
66			 : "=r"(ret0)
67			 : "r"(which), "r"(in0), "r"(in1), "r"(in2), "r"(in3));
68	return ret0;
69}
70
71#ifndef SSC_NSECT
72#define SSC_NSECT 409600
73#endif
74
75static MALLOC_DEFINE(M_SSC, "ssc_disk", "Simulator Disk");
76
77static d_strategy_t sscstrategy;
78
79static LIST_HEAD(, ssc_s) ssc_softc_list = LIST_HEAD_INITIALIZER(ssc_softc_list);
80
81struct ssc_s {
82	int unit;
83	LIST_ENTRY(ssc_s) list;
84	struct bio_queue_head bio_queue;
85	struct disk *disk;
86	struct cdev *dev;
87	int busy;
88	int fd;
89};
90
91static int sscunits;
92
93static void
94sscstrategy(struct bio *bp)
95{
96	struct ssc_s *sc;
97	int s;
98	struct disk_req req;
99	struct disk_stat stat;
100	u_long len, va, off;
101
102	sc = bp->bio_disk->d_drv1;
103
104	s = splbio();
105
106	bioq_disksort(&sc->bio_queue, bp);
107
108	if (sc->busy) {
109		splx(s);
110		return;
111	}
112
113	sc->busy++;
114
115	while (1) {
116		bp = bioq_takefirst(&sc->bio_queue);
117		splx(s);
118		if (!bp)
119			break;
120
121		va = (u_long) bp->bio_data;
122		len = bp->bio_bcount;
123		off = bp->bio_pblkno << DEV_BSHIFT;
124		while (len > 0) {
125			u_int t;
126			if ((va & PAGE_MASK) + len > PAGE_SIZE)
127				t = PAGE_SIZE - (va & PAGE_MASK);
128			else
129				t = len;
130			req.len = t;
131			req.addr = ia64_tpa(va);
132			ssc(sc->fd, 1, ia64_tpa((long) &req), off,
133			    (bp->bio_cmd == BIO_READ) ? SSC_READ : SSC_WRITE);
134			stat.fd = sc->fd;
135			ssc(ia64_tpa((long)&stat), 0, 0, 0,
136			    SSC_WAIT_COMPLETION);
137			va += t;
138			len -= t;
139			off += t;
140		}
141		bp->bio_resid = 0;
142		biodone(bp);
143		s = splbio();
144	}
145
146	sc->busy = 0;
147	return;
148}
149
150static struct ssc_s *
151ssccreate(int unit)
152{
153	struct ssc_s *sc;
154	int fd;
155
156	fd = ssc(ia64_tpa((u_int64_t) SKI_ROOT_FILESYSTEM),
157		 1, 0, 0, SSC_OPEN);
158	if (fd == -1)
159		return (NULL);
160
161	if (unit == -1)
162		unit = sscunits++;
163	/* Make sure this unit isn't already in action */
164	LIST_FOREACH(sc, &ssc_softc_list, list) {
165		if (sc->unit == unit)
166			return (NULL);
167	}
168	sc = malloc(sizeof(*sc), M_SSC, M_WAITOK | M_ZERO);
169	LIST_INSERT_HEAD(&ssc_softc_list, sc, list);
170	sc->unit = unit;
171	bioq_init(&sc->bio_queue);
172
173	sc->disk = disk_alloc();
174	sc->disk->d_drv1 = sc;
175	sc->disk->d_fwheads = 0;
176	sc->disk->d_fwsectors = 0;
177	sc->disk->d_maxsize = DFLTPHYS;
178	sc->disk->d_mediasize = (off_t)SSC_NSECT * DEV_BSIZE;
179	sc->disk->d_name = "sscdisk";
180	sc->disk->d_sectorsize = DEV_BSIZE;
181	sc->disk->d_strategy = sscstrategy;
182	sc->disk->d_unit = sc->unit;
183	sc->disk->d_flags = DISKFLAG_NEEDSGIANT;
184	disk_create(sc->disk, DISK_VERSION);
185	sc->fd = fd;
186	if (sc->unit == 0)
187		rootdevnames[0] = "ufs:/dev/sscdisk0";
188	return (sc);
189}
190
191static void
192ssc_drvinit(void *unused)
193{
194	ssccreate(-1);
195}
196
197SYSINIT(sscdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE, ssc_drvinit,NULL);
198