Skip to content
This repository was archived by the owner on Jul 3, 2020. It is now read-only.

Commit 433d9c4

Browse files
committed
WIP disk driver
1 parent b177050 commit 433d9c4

8 files changed

Lines changed: 311 additions & 1 deletion

File tree

js/core/disk/disk-driver.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright 2016-present runtime.js project authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
'use strict';
16+
17+
const nameHandle = Symbol('name');
18+
19+
class DiskDriver {
20+
constructor(name = '') {
21+
this[nameHandle] = name;
22+
this.onread = null;
23+
this.onwrite = null;
24+
this.ongetformatinfo = null;
25+
}
26+
get name() {
27+
return this[nameHandle];
28+
}
29+
read(sector, u8) {
30+
if (!this.onread) {
31+
throw new Error('driver was not initialized');
32+
}
33+
return this.onread(sector, u8);
34+
}
35+
write(sector, u8) {
36+
if (!this.onwrite) {
37+
throw new Error('driver was not initialized');
38+
}
39+
return this.onwrite(sector, u8);
40+
}
41+
get formatInfo() {
42+
if (!this.ongetformatinfo) {
43+
throw new Error('driver was not initialized');
44+
}
45+
return this.ongetformatinfo();
46+
}
47+
}
48+
49+
module.exports = DiskDriver;

js/core/disk/drivers.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2016-present runtime.js project authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
'use strict';
16+
let defaultDriver = null;
17+
const availableDrivers = Object.create(null);
18+
19+
function deepFreeze(obj) {
20+
const keys = Object.getOwnPropertyNames(obj);
21+
for (const key of keys) {
22+
const prop = obj[key];
23+
if (typeof prop === 'object' && prop !== null) deepFreeze(prop);
24+
}
25+
return Object.freeze(obj);
26+
}
27+
28+
exports.addDriver = (driver) => {
29+
availableDrivers[driver.name] = driver;
30+
31+
console.log(`[disk] using driver ${driver.name}`);
32+
33+
// Set this driver as the default one
34+
defaultDriver = driver;
35+
};
36+
37+
exports.getDefaultDriver = () => defaultDriver;
38+
exports.getCopyOfDrivers = () => {
39+
const copy = Object.create(null);
40+
Object.assign(copy, availableDrivers);
41+
return deepFreeze(copy);
42+
};

js/core/disk/index.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2016-present runtime.js project authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
'use strict';
16+
const DiskDriver = require('./disk-driver');
17+
const { addDriver, getDefaultDriver, getCopyOfDrivers } = require('./drivers');
18+
19+
module.exports = {
20+
DiskDriver,
21+
addDriver,
22+
read(sector, data) {
23+
return getDefaultDriver().read(sector, data);
24+
},
25+
write(sector, data) {
26+
return getDefaultDriver().write(sector, data);
27+
},
28+
get formatInfo() {
29+
return getDefaultDriver().formatInfo;
30+
},
31+
// we expose drivers because the user may want
32+
// to access different drives with different drivers
33+
get drivers() {
34+
return getCopyOfDrivers();
35+
}
36+
};

js/core/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const { allocator } = require('./resources');
1818
require('./polyfill');
1919

2020
const random = require('./random');
21+
const disk = require('./disk');
2122
const keyboard = require('./keyboard');
2223
const ps2 = require('./ps2');
2324
const pci = require('./pci');
@@ -28,6 +29,7 @@ class Runtime {
2829
constructor() {
2930
Object.assign(this, {
3031
random,
32+
disk,
3133
keyboard,
3234
pci,
3335
ps2,

js/driver/virtio/blk.js

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// Copyright 2016-present runtime.js project authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
'use strict';
16+
const VirtioDevice = require('./device');
17+
const runtime = require('../../core');
18+
19+
const VIRTIO_BLK_T_IN = 0;
20+
const VIRTIO_BLK_T_OUT = 1;
21+
22+
// not sure how to set uint64
23+
function setUint64LE(u8, offset, value) {
24+
u8[offset] = (value >>> 0);
25+
u8[offset + 1] = (value >>> 8);
26+
u8[offset + 2] = (value >>> 16);
27+
u8[offset + 3] = (value >>> 24);
28+
u8[offset + 4] = (value >>> 32);
29+
u8[offset + 5] = (value >>> 40);
30+
u8[offset + 6] = (value >>> 48);
31+
u8[offset + 7] = (value >>> 56);
32+
}
33+
34+
function initializeBlockDevice(pciDevice) {
35+
const ioSpace = pciDevice.getBAR(0).resource;
36+
const irq = pciDevice.getIRQ();
37+
38+
const features = {
39+
VIRTIO_BLK_F_SIZE_MAX: 1,
40+
VIRTIO_BLK_F_SEG_MAX: 2,
41+
VIRTIO_BLK_F_GEOMETRY: 4,
42+
VIRTIO_BLK_F_RO: 5,
43+
VIRTIO_BLK_F_BLK_SIZE: 6,
44+
VIRTIO_BLK_F_FLUSH: 9,
45+
VIRTIO_BLK_F_TOPOLOGY: 10,
46+
VIRTIO_BLK_F_CONFIG_WCE: 11,
47+
};
48+
49+
const dev = new VirtioDevice('blk', ioSpace);
50+
dev.setDriverAck();
51+
52+
const driverFeatures = {
53+
// VIRTIO_BLK_F_SIZE_MAX: true,
54+
VIRTIO_BLK_F_SEG_MAX: true,
55+
VIRTIO_BLK_F_GEOMETRY: true,
56+
VIRTIO_BLK_F_BLK_SIZE: true,
57+
VIRTIO_BLK_F_TOPOLOGY: true,
58+
};
59+
60+
const deviceFeatures = dev.readDeviceFeatures(features);
61+
debug(JSON.stringify(deviceFeatures));
62+
63+
if (!dev.writeGuestFeatures(features, driverFeatures, deviceFeatures)) {
64+
debug('[virtio] blk driver is unable to start');
65+
return;
66+
}
67+
68+
const QUEUE_ID_REQ = 0;
69+
70+
const reqQueue = dev.queueSetup(QUEUE_ID_REQ);
71+
const promiseQueue = [];
72+
73+
const sectorSize = 512;
74+
const sectorCount = dev.blkReadSectorCount();
75+
const totalSectorCount = dev.blkReadTotalSectorCount();
76+
77+
function buildHeader(type, sector) {
78+
const u8 = new Uint8Array(16);
79+
const view = new DataView(u8.buffer);
80+
view.setUint32(0, type, true);
81+
view.setUint32(4, 0, true); // priority: low
82+
//setUint64LE(u8, 8, sector); // doesn't work as expected
83+
view.setFloat64(8, sector, true); // DataView doesn't have setUint64!
84+
return u8;
85+
}
86+
87+
const diskDriver = new runtime.disk.DiskDriver('blk');
88+
diskDriver.onread = (sector, data) => {
89+
return new Promise((resolve, reject) => {
90+
if (sector > totalSectorCount) {
91+
setImmediate(() => {
92+
reject(new RangeError(`sector out of bounds (max: ${totalSectorCount})`));
93+
});
94+
return;
95+
}
96+
const status = new Uint8Array(1);
97+
promiseQueue.push([resolve, reject, VIRTIO_BLK_T_IN, data, status]);
98+
reqQueue.placeBuffers([buildHeader(VIRTIO_BLK_T_IN, sector), data, status], [false, true, true]);
99+
100+
if (reqQueue.isNotificationNeeded()) {
101+
dev.queueNotify(QUEUE_ID_REQ);
102+
}
103+
});
104+
};
105+
diskDriver.onwrite = (sector, data) => {
106+
return new Promise((resolve, reject) => {
107+
const status = new Uint8Array(1);
108+
promiseQueue.push([resolve, reject, VIRTIO_BLK_T_OUT, data, status]);
109+
reqQueue.placeBuffers([buildHeader(VIRTIO_BLK_T_OUT, sector), data, status], [false, false, true]);
110+
111+
if (reqQueue.isNotificationNeeded()) {
112+
dev.queueNotify(QUEUE_ID_REQ);
113+
}
114+
});
115+
};
116+
diskDriver.ongetformatinfo = () => ({
117+
sectorSize,
118+
sectorCount,
119+
totalSectorCount,
120+
});
121+
122+
runtime.disk.addDriver(diskDriver);
123+
124+
function recvBuffer() {
125+
if (promiseQueue.length === 0) {
126+
return;
127+
}
128+
const [resolve, reject, type, data, status] = promiseQueue.shift();
129+
setImmediate(() => {
130+
if (status[0] !== 0) return reject(new Error('IO error'));
131+
if (type === VIRTIO_BLK_T_IN) {
132+
resolve(data);
133+
} else {
134+
resolve();
135+
}
136+
});
137+
}
138+
139+
irq.on(() => {
140+
if (!dev.hasPendingIRQ()) {
141+
return;
142+
}
143+
reqQueue.fetchBuffers(recvBuffer);
144+
});
145+
146+
dev.setDriverReady();
147+
}
148+
149+
module.exports = initializeBlockDevice;

js/driver/virtio/device.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,17 @@ class VirtioDevice {
4747
ioPorts.NETWORK_DEVICE_STATUS = 0x1A; // 16 bit r
4848
}
4949

50+
if (deviceType === 'blk') {
51+
// Block device
52+
ioPorts.BLOCK_TOTAL_SECTOR_COUNT = 0x14; // 64 bit r
53+
ioPorts.BLOCK_MAX_SEGMENT_SIZE = 0x1c; // 32 bit r
54+
ioPorts.BLOCK_MAX_SEGMENT_COUNT = 0x20; // 32 bit r
55+
ioPorts.BLOCK_CYLINDER_COUNT = 0x24; // 16 bit r
56+
ioPorts.BLOCK_HEAD_COUNT = 0x26; // 8 bit r
57+
ioPorts.BLOCK_SECTOR_COUNT = 0x27; // 8 bit r
58+
ioPorts.BLOCK_BLOCK_LENGTH = 0x28; // 32 bit r
59+
}
60+
5061
const ports = {};
5162
for (const portName of Object.keys(ioPorts)) {
5263
const portOffset = ioPorts[portName];
@@ -128,6 +139,16 @@ class VirtioDevice {
128139
netReadStatus() {
129140
return !!(1 & this.io.NETWORK_DEVICE_STATUS.read16());
130141
}
142+
143+
// [block device]
144+
blkReadSectorCount() {
145+
return this.io.BLOCK_SECTOR_COUNT.read8();
146+
}
147+
148+
// [block device]
149+
blkReadTotalSectorCount() {
150+
return this.io.BLOCK_TOTAL_SECTOR_COUNT.read32();
151+
}
131152
}
132153

133154
module.exports = VirtioDevice;

js/driver/virtio/index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414

1515
'use strict';
1616
const virtioNet = require('./net');
17+
const virtioBlk = require('./blk');
1718
const virtioRNG = require('./rng');
1819
const VIRTIO_SUBSYSTEM_NETWORK = 1;
20+
const VIRTIO_SUBSYSTEM_BLOCK = 2;
1921
const VIRTIO_SUBSYSTEM_RNG = 4;
2022
const runtime = require('../../core');
2123

@@ -26,6 +28,9 @@ const driver = {
2628
if (subsystemId === VIRTIO_SUBSYSTEM_NETWORK) {
2729
return virtioNet(pciDevice);
2830
}
31+
if (subsystemId === VIRTIO_SUBSYSTEM_BLOCK) {
32+
return virtioBlk(pciDevice);
33+
}
2934
if (subsystemId === VIRTIO_SUBSYSTEM_RNG) {
3035
return virtioRNG(pciDevice);
3136
}

js/driver/virtio/vring/descriptor-table.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,17 @@ class DescriptorTable {
9595
const first = head;
9696
for (let i = 0; i < count; ++i) {
9797
const d = buffers[i];
98+
let bufWriteOnly = false;
99+
if (typeof isWriteOnly === 'boolean') {
100+
bufWriteOnly = isWriteOnly;
101+
} else {
102+
bufWriteOnly = isWriteOnly[i];
103+
}
98104
let flags = 0;
99105
if (count !== i + 1) {
100106
flags |= VRING_DESC_F_NEXT;
101107
}
102-
if (isWriteOnly) {
108+
if (bufWriteOnly) {
103109
flags |= VRING_DESC_F_WRITE;
104110
}
105111

0 commit comments

Comments
 (0)