summaryrefslogtreecommitdiffstats
path: root/pintos-progos/devices
diff options
context:
space:
mode:
Diffstat (limited to 'pintos-progos/devices')
-rw-r--r--pintos-progos/devices/block.c223
-rw-r--r--pintos-progos/devices/block.h74
-rw-r--r--pintos-progos/devices/ide.c527
-rw-r--r--pintos-progos/devices/ide.h6
-rw-r--r--pintos-progos/devices/input.c52
-rw-r--r--pintos-progos/devices/input.h12
-rw-r--r--pintos-progos/devices/intq.c114
-rw-r--r--pintos-progos/devices/intq.h43
-rw-r--r--pintos-progos/devices/kbd.c213
-rw-r--r--pintos-progos/devices/kbd.h9
-rw-r--r--pintos-progos/devices/partition.c324
-rw-r--r--pintos-progos/devices/partition.h8
-rw-r--r--pintos-progos/devices/pit.c83
-rw-r--r--pintos-progos/devices/pit.h8
-rw-r--r--pintos-progos/devices/rtc.c112
-rw-r--r--pintos-progos/devices/rtc.h8
-rw-r--r--pintos-progos/devices/serial.c228
-rw-r--r--pintos-progos/devices/serial.h11
-rw-r--r--pintos-progos/devices/shutdown.c131
-rw-r--r--pintos-progos/devices/shutdown.h19
-rw-r--r--pintos-progos/devices/speaker.c68
-rw-r--r--pintos-progos/devices/speaker.h8
-rw-r--r--pintos-progos/devices/timer.c246
-rw-r--r--pintos-progos/devices/timer.h29
-rw-r--r--pintos-progos/devices/vga.c172
-rw-r--r--pintos-progos/devices/vga.h6
26 files changed, 2734 insertions, 0 deletions
diff --git a/pintos-progos/devices/block.c b/pintos-progos/devices/block.c
new file mode 100644
index 0000000..a3acec1
--- /dev/null
+++ b/pintos-progos/devices/block.c
@@ -0,0 +1,223 @@
1#include "devices/block.h"
2#include <list.h>
3#include <string.h>
4#include <stdio.h>
5#include "devices/ide.h"
6#include "threads/malloc.h"
7
8/* A block device. */
9struct block
10 {
11 struct list_elem list_elem; /* Element in all_blocks. */
12
13 char name[16]; /* Block device name. */
14 enum block_type type; /* Type of block device. */
15 block_sector_t size; /* Size in sectors. */
16
17 const struct block_operations *ops; /* Driver operations. */
18 void *aux; /* Extra data owned by driver. */
19
20 unsigned long long read_cnt; /* Number of sectors read. */
21 unsigned long long write_cnt; /* Number of sectors written. */
22 };
23
24/* List of all block devices. */
25static struct list all_blocks = LIST_INITIALIZER (all_blocks);
26
27/* The block block assigned to each Pintos role. */
28static struct block *block_by_role[BLOCK_ROLE_CNT];
29
30static struct block *list_elem_to_block (struct list_elem *);
31
32/* Returns a human-readable name for the given block device
33 TYPE. */
34const char *
35block_type_name (enum block_type type)
36{
37 static const char *block_type_names[BLOCK_CNT] =
38 {
39 "kernel",
40 "filesys",
41 "scratch",
42 "swap",
43 "raw",
44 "foreign",
45 };
46
47 ASSERT (type < BLOCK_CNT);
48 return block_type_names[type];
49}
50
51/* Returns the block device fulfilling the given ROLE, or a null
52 pointer if no block device has been assigned that role. */
53struct block *
54block_get_role (enum block_type role)
55{
56 ASSERT (role < BLOCK_ROLE_CNT);
57 return block_by_role[role];
58}
59
60/* Assigns BLOCK the given ROLE. */
61void
62block_set_role (enum block_type role, struct block *block)
63{
64 ASSERT (role < BLOCK_ROLE_CNT);
65 block_by_role[role] = block;
66}
67
68/* Returns the first block device in kernel probe order, or a
69 null pointer if no block devices are registered. */
70struct block *
71block_first (void)
72{
73 return list_elem_to_block (list_begin (&all_blocks));
74}
75
76/* Returns the block device following BLOCK in kernel probe
77 order, or a null pointer if BLOCK is the last block device. */
78struct block *
79block_next (struct block *block)
80{
81 return list_elem_to_block (list_next (&block->list_elem));
82}
83
84/* Returns the block device with the given NAME, or a null
85 pointer if no block device has that name. */
86struct block *
87block_get_by_name (const char *name)
88{
89 struct list_elem *e;
90
91 for (e = list_begin (&all_blocks); e != list_end (&all_blocks);
92 e = list_next (e))
93 {
94 struct block *block = list_entry (e, struct block, list_elem);
95 if (!strcmp (name, block->name))
96 return block;
97 }
98
99 return NULL;
100}
101
102/* Verifies that SECTOR is a valid offset within BLOCK.
103 Panics if not. */
104static void
105check_sector (struct block *block, block_sector_t sector)
106{
107 if (sector >= block->size)
108 {
109 /* We do not use ASSERT because we want to panic here
110 regardless of whether NDEBUG is defined. */
111 PANIC ("Access past end of device %s (sector=%"PRDSNu", "
112 "size=%"PRDSNu")\n", block_name (block), sector, block->size);
113 }
114}
115
116/* Reads sector SECTOR from BLOCK into BUFFER, which must
117 have room for BLOCK_SECTOR_SIZE bytes.
118 Internally synchronizes accesses to block devices, so external
119 per-block device locking is unneeded. */
120void
121block_read (struct block *block, block_sector_t sector, void *buffer)
122{
123 check_sector (block, sector);
124 block->ops->read (block->aux, sector, buffer);
125 block->read_cnt++;
126}
127
128/* Write sector SECTOR to BLOCK from BUFFER, which must contain
129 BLOCK_SECTOR_SIZE bytes. Returns after the block device has
130 acknowledged receiving the data.
131 Internally synchronizes accesses to block devices, so external
132 per-block device locking is unneeded. */
133void
134block_write (struct block *block, block_sector_t sector, const void *buffer)
135{
136 check_sector (block, sector);
137 ASSERT (block->type != BLOCK_FOREIGN);
138 block->ops->write (block->aux, sector, buffer);
139 block->write_cnt++;
140}
141
142/* Returns the number of sectors in BLOCK. */
143block_sector_t
144block_size (struct block *block)
145{
146 return block->size;
147}
148
149/* Returns BLOCK's name (e.g. "hda"). */
150const char *
151block_name (struct block *block)
152{
153 return block->name;
154}
155
156/* Returns BLOCK's type. */
157enum block_type
158block_type (struct block *block)
159{
160 return block->type;
161}
162
163/* Prints statistics for each block device used for a Pintos role. */
164void
165block_print_stats (void)
166{
167 int i;
168
169 for (i = 0; i < BLOCK_ROLE_CNT; i++)
170 {
171 struct block *block = block_by_role[i];
172 if (block != NULL)
173 {
174 printf ("%s (%s): %llu reads, %llu writes\n",
175 block->name, block_type_name (block->type),
176 block->read_cnt, block->write_cnt);
177 }
178 }
179}
180
181/* Registers a new block device with the given NAME. If
182 EXTRA_INFO is non-null, it is printed as part of a user
183 message. The block device's SIZE in sectors and its TYPE must
184 be provided, as well as the it operation functions OPS, which
185 will be passed AUX in each function call. */
186struct block *
187block_register (const char *name, enum block_type type,
188 const char *extra_info, block_sector_t size,
189 const struct block_operations *ops, void *aux)
190{
191 struct block *block = malloc (sizeof *block);
192 if (block == NULL)
193 PANIC ("Failed to allocate memory for block device descriptor");
194
195 list_push_back (&all_blocks, &block->list_elem);
196 strlcpy (block->name, name, sizeof block->name);
197 block->type = type;
198 block->size = size;
199 block->ops = ops;
200 block->aux = aux;
201 block->read_cnt = 0;
202 block->write_cnt = 0;
203
204 printf ("%s: %'"PRDSNu" sectors (", block->name, block->size);
205 print_human_readable_size ((uint64_t) block->size * BLOCK_SECTOR_SIZE);
206 printf (")");
207 if (extra_info != NULL)
208 printf (", %s", extra_info);
209 printf ("\n");
210
211 return block;
212}
213
214/* Returns the block device corresponding to LIST_ELEM, or a null
215 pointer if LIST_ELEM is the list end of all_blocks. */
216static struct block *
217list_elem_to_block (struct list_elem *list_elem)
218{
219 return (list_elem != list_end (&all_blocks)
220 ? list_entry (list_elem, struct block, list_elem)
221 : NULL);
222}
223
diff --git a/pintos-progos/devices/block.h b/pintos-progos/devices/block.h
new file mode 100644
index 0000000..21732d6
--- /dev/null
+++ b/pintos-progos/devices/block.h
@@ -0,0 +1,74 @@
1#ifndef DEVICES_BLOCK_H
2#define DEVICES_BLOCK_H
3
4#include <stddef.h>
5#include <inttypes.h>
6
7/* Size of a block device sector in bytes.
8 All IDE disks use this sector size, as do most USB and SCSI
9 disks. It's not worth it to try to cater to other sector
10 sizes in Pintos (yet). */
11#define BLOCK_SECTOR_SIZE 512
12
13/* Index of a block device sector.
14 Good enough for devices up to 2 TB. */
15typedef uint32_t block_sector_t;
16
17/* Format specifier for printf(), e.g.:
18 printf ("sector=%"PRDSNu"\n", sector); */
19#define PRDSNu PRIu32
20
21/* Higher-level interface for file systems, etc. */
22
23struct block;
24
25/* Type of a block device. */
26enum block_type
27 {
28 /* Block device types that play a role in Pintos. */
29 BLOCK_KERNEL, /* Pintos OS kernel. */
30 BLOCK_FILESYS, /* File system. */
31 BLOCK_SCRATCH, /* Scratch. */
32 BLOCK_SWAP, /* Swap. */
33 BLOCK_ROLE_CNT,
34
35 /* Other kinds of block devices that Pintos may see but does
36 not interact with. */
37 BLOCK_RAW = BLOCK_ROLE_CNT, /* "Raw" device with unidentified contents. */
38 BLOCK_FOREIGN, /* Owned by non-Pintos operating system. */
39 BLOCK_CNT /* Number of Pintos block types. */
40 };
41
42const char *block_type_name (enum block_type);
43
44/* Finding block devices. */
45struct block *block_get_role (enum block_type);
46void block_set_role (enum block_type, struct block *);
47struct block *block_get_by_name (const char *name);
48
49struct block *block_first (void);
50struct block *block_next (struct block *);
51
52/* Block device operations. */
53block_sector_t block_size (struct block *);
54void block_read (struct block *, block_sector_t, void *);
55void block_write (struct block *, block_sector_t, const void *);
56const char *block_name (struct block *);
57enum block_type block_type (struct block *);
58
59/* Statistics. */
60void block_print_stats (void);
61
62/* Lower-level interface to block device drivers. */
63
64struct block_operations
65 {
66 void (*read) (void *aux, block_sector_t, void *buffer);
67 void (*write) (void *aux, block_sector_t, const void *buffer);
68 };
69
70struct block *block_register (const char *name, enum block_type,
71 const char *extra_info, block_sector_t size,
72 const struct block_operations *, void *aux);
73
74#endif /* devices/block.h */
diff --git a/pintos-progos/devices/ide.c b/pintos-progos/devices/ide.c
new file mode 100644
index 0000000..2cc0292
--- /dev/null
+++ b/pintos-progos/devices/ide.c
@@ -0,0 +1,527 @@
1#include "devices/ide.h"
2#include <ctype.h>
3#include <debug.h>
4#include <stdbool.h>
5#include <stdio.h>
6#include "devices/block.h"
7#include "devices/partition.h"
8#include "devices/timer.h"
9#include "threads/io.h"
10#include "threads/interrupt.h"
11#include "threads/synch.h"
12
13/* The code in this file is an interface to an ATA (IDE)
14 controller. It attempts to comply to [ATA-3]. */
15
16/* ATA command block port addresses. */
17#define reg_data(CHANNEL) ((CHANNEL)->reg_base + 0) /* Data. */
18#define reg_error(CHANNEL) ((CHANNEL)->reg_base + 1) /* Error. */
19#define reg_nsect(CHANNEL) ((CHANNEL)->reg_base + 2) /* Sector Count. */
20#define reg_lbal(CHANNEL) ((CHANNEL)->reg_base + 3) /* LBA 0:7. */
21#define reg_lbam(CHANNEL) ((CHANNEL)->reg_base + 4) /* LBA 15:8. */
22#define reg_lbah(CHANNEL) ((CHANNEL)->reg_base + 5) /* LBA 23:16. */
23#define reg_device(CHANNEL) ((CHANNEL)->reg_base + 6) /* Device/LBA 27:24. */
24#define reg_status(CHANNEL) ((CHANNEL)->reg_base + 7) /* Status (r/o). */
25#define reg_command(CHANNEL) reg_status (CHANNEL) /* Command (w/o). */
26
27/* ATA control block port addresses.
28 (If we supported non-legacy ATA controllers this would not be
29 flexible enough, but it's fine for what we do.) */
30#define reg_ctl(CHANNEL) ((CHANNEL)->reg_base + 0x206) /* Control (w/o). */
31#define reg_alt_status(CHANNEL) reg_ctl (CHANNEL) /* Alt Status (r/o). */
32
33/* Alternate Status Register bits. */
34#define STA_BSY 0x80 /* Busy. */
35#define STA_DRDY 0x40 /* Device Ready. */
36#define STA_DRQ 0x08 /* Data Request. */
37
38/* Control Register bits. */
39#define CTL_SRST 0x04 /* Software Reset. */
40
41/* Device Register bits. */
42#define DEV_MBS 0xa0 /* Must be set. */
43#define DEV_LBA 0x40 /* Linear based addressing. */
44#define DEV_DEV 0x10 /* Select device: 0=master, 1=slave. */
45
46/* Commands.
47 Many more are defined but this is the small subset that we
48 use. */
49#define CMD_IDENTIFY_DEVICE 0xec /* IDENTIFY DEVICE. */
50#define CMD_READ_SECTOR_RETRY 0x20 /* READ SECTOR with retries. */
51#define CMD_WRITE_SECTOR_RETRY 0x30 /* WRITE SECTOR with retries. */
52
53/* An ATA device. */
54struct ata_disk
55 {
56 char name[8]; /* Name, e.g. "hda". */
57 struct channel *channel; /* Channel that disk is attached to. */
58 int dev_no; /* Device 0 or 1 for master or slave. */
59 bool is_ata; /* Is device an ATA disk? */
60 };
61
62/* An ATA channel (aka controller).
63 Each channel can control up to two disks. */
64struct channel
65 {
66 char name[8]; /* Name, e.g. "ide0". */
67 uint16_t reg_base; /* Base I/O port. */
68 uint8_t irq; /* Interrupt in use. */
69
70 struct lock lock; /* Must acquire to access the controller. */
71 bool expecting_interrupt; /* True if an interrupt is expected, false if
72 any interrupt would be spurious. */
73 struct semaphore completion_wait; /* Up'd by interrupt handler. */
74
75 struct ata_disk devices[2]; /* The devices on this channel. */
76 };
77
78/* We support the two "legacy" ATA channels found in a standard PC. */
79#define CHANNEL_CNT 2
80static struct channel channels[CHANNEL_CNT];
81
82static struct block_operations ide_operations;
83
84static void reset_channel (struct channel *);
85static bool check_device_type (struct ata_disk *);
86static void identify_ata_device (struct ata_disk *);
87
88static void select_sector (struct ata_disk *, block_sector_t);
89static void issue_pio_command (struct channel *, uint8_t command);
90static void input_sector (struct channel *, void *);
91static void output_sector (struct channel *, const void *);
92
93static void wait_until_idle (const struct ata_disk *);
94static bool wait_while_busy (const struct ata_disk *);
95static void select_device (const struct ata_disk *);
96static void select_device_wait (const struct ata_disk *);
97
98static void interrupt_handler (struct intr_frame *);
99
100/* Initialize the disk subsystem and detect disks. */
101void
102ide_init (void)
103{
104 size_t chan_no;
105
106 for (chan_no = 0; chan_no < CHANNEL_CNT; chan_no++)
107 {
108 struct channel *c = &channels[chan_no];
109 int dev_no;
110
111 /* Initialize channel. */
112 snprintf (c->name, sizeof c->name, "ide%zu", chan_no);
113 switch (chan_no)
114 {
115 case 0:
116 c->reg_base = 0x1f0;
117 c->irq = 14 + 0x20;
118 break;
119 case 1:
120 c->reg_base = 0x170;
121 c->irq = 15 + 0x20;
122 break;
123 default:
124 NOT_REACHED ();
125 }
126 lock_init (&c->lock);
127 c->expecting_interrupt = false;
128 sema_init (&c->completion_wait, 0);
129
130 /* Initialize devices. */
131 for (dev_no = 0; dev_no < 2; dev_no++)
132 {
133 struct ata_disk *d = &c->devices[dev_no];
134 snprintf (d->name, sizeof d->name,
135 "hd%c", 'a' + chan_no * 2 + dev_no);
136 d->channel = c;
137 d->dev_no = dev_no;
138 d->is_ata = false;
139 }
140
141 /* Register interrupt handler. */
142 intr_register_ext (c->irq, interrupt_handler, c->name);
143
144 /* Reset hardware. */
145 reset_channel (c);
146
147 /* Distinguish ATA hard disks from other devices. */
148 if (check_device_type (&c->devices[0]))
149 check_device_type (&c->devices[1]);
150
151 /* Read hard disk identity information. */
152 for (dev_no = 0; dev_no < 2; dev_no++)
153 if (c->devices[dev_no].is_ata)
154 identify_ata_device (&c->devices[dev_no]);
155 }
156}
157
158/* Disk detection and identification. */
159
160static char *descramble_ata_string (char *, int size);
161
162/* Resets an ATA channel and waits for any devices present on it
163 to finish the reset. */
164static void
165reset_channel (struct channel *c)
166{
167 bool present[2];
168 int dev_no;
169
170 /* The ATA reset sequence depends on which devices are present,
171 so we start by detecting device presence. */
172 for (dev_no = 0; dev_no < 2; dev_no++)
173 {
174 struct ata_disk *d = &c->devices[dev_no];
175
176 select_device (d);
177
178 outb (reg_nsect (c), 0x55);
179 outb (reg_lbal (c), 0xaa);
180
181 outb (reg_nsect (c), 0xaa);
182 outb (reg_lbal (c), 0x55);
183
184 outb (reg_nsect (c), 0x55);
185 outb (reg_lbal (c), 0xaa);
186
187 present[dev_no] = (inb (reg_nsect (c)) == 0x55
188 && inb (reg_lbal (c)) == 0xaa);
189 }
190
191 /* Issue soft reset sequence, which selects device 0 as a side effect.
192 Also enable interrupts. */
193 outb (reg_ctl (c), 0);
194 timer_usleep (10);
195 outb (reg_ctl (c), CTL_SRST);
196 timer_usleep (10);
197 outb (reg_ctl (c), 0);
198
199 timer_msleep (150);
200
201 /* Wait for device 0 to clear BSY. */
202 if (present[0])
203 {
204 select_device (&c->devices[0]);
205 wait_while_busy (&c->devices[0]);
206 }
207
208 /* Wait for device 1 to clear BSY. */
209 if (present[1])
210 {
211 int i;
212
213 select_device (&c->devices[1]);
214 for (i = 0; i < 3000; i++)
215 {
216 if (inb (reg_nsect (c)) == 1 && inb (reg_lbal (c)) == 1)
217 break;
218 timer_msleep (10);
219 }
220 wait_while_busy (&c->devices[1]);
221 }
222}
223
224/* Checks whether device D is an ATA disk and sets D's is_ata
225 member appropriately. If D is device 0 (master), returns true
226 if it's possible that a slave (device 1) exists on this
227 channel. If D is device 1 (slave), the return value is not
228 meaningful. */
229static bool
230check_device_type (struct ata_disk *d)
231{
232 struct channel *c = d->channel;
233 uint8_t error, lbam, lbah, status;
234
235 select_device (d);
236
237 error = inb (reg_error (c));
238 lbam = inb (reg_lbam (c));
239 lbah = inb (reg_lbah (c));
240 status = inb (reg_status (c));
241
242 if ((error != 1 && (error != 0x81 || d->dev_no == 1))
243 || (status & STA_DRDY) == 0
244 || (status & STA_BSY) != 0)
245 {
246 d->is_ata = false;
247 return error != 0x81;
248 }
249 else
250 {
251 d->is_ata = (lbam == 0 && lbah == 0) || (lbam == 0x3c && lbah == 0xc3);
252 return true;
253 }
254}
255
256/* Sends an IDENTIFY DEVICE command to disk D and reads the
257 response. Registers the disk with the block device
258 layer. */
259static void
260identify_ata_device (struct ata_disk *d)
261{
262 struct channel *c = d->channel;
263 char id[BLOCK_SECTOR_SIZE];
264 block_sector_t capacity;
265 char *model, *serial;
266 char extra_info[128];
267 struct block *block;
268
269 ASSERT (d->is_ata);
270
271 /* Send the IDENTIFY DEVICE command, wait for an interrupt
272 indicating the device's response is ready, and read the data
273 into our buffer. */
274 select_device_wait (d);
275 issue_pio_command (c, CMD_IDENTIFY_DEVICE);
276 sema_down (&c->completion_wait);
277 if (!wait_while_busy (d))
278 {
279 d->is_ata = false;
280 return;
281 }
282 input_sector (c, id);
283
284 /* Calculate capacity.
285 Read model name and serial number. */
286 capacity = *(uint32_t *) &id[60 * 2];
287 model = descramble_ata_string (&id[10 * 2], 20);
288 serial = descramble_ata_string (&id[27 * 2], 40);
289 snprintf (extra_info, sizeof extra_info,
290 "model \"%s\", serial \"%s\"", model, serial);
291
292 /* Disable access to IDE disks over 1 GB, which are likely
293 physical IDE disks rather than virtual ones. If we don't
294 allow access to those, we're less likely to scribble on
295 someone's important data. You can disable this check by
296 hand if you really want to do so. */
297 if (capacity >= 1024 * 1024 * 1024 / BLOCK_SECTOR_SIZE)
298 {
299 printf ("%s: ignoring ", d->name);
300 print_human_readable_size (capacity * 512);
301 printf ("disk for safety\n");
302 d->is_ata = false;
303 return;
304 }
305
306 /* Register. */
307 block = block_register (d->name, BLOCK_RAW, extra_info, capacity,
308 &ide_operations, d);
309 partition_scan (block);
310}
311
312/* Translates STRING, which consists of SIZE bytes in a funky
313 format, into a null-terminated string in-place. Drops
314 trailing whitespace and null bytes. Returns STRING. */
315static char *
316descramble_ata_string (char *string, int size)
317{
318 int i;
319
320 /* Swap all pairs of bytes. */
321 for (i = 0; i + 1 < size; i += 2)
322 {
323 char tmp = string[i];
324 string[i] = string[i + 1];
325 string[i + 1] = tmp;
326 }
327
328 /* Find the last non-white, non-null character. */
329 for (size--; size > 0; size--)
330 {
331 int c = string[size - 1];
332 if (c != '\0' && !isspace (c))
333 break;
334 }
335 string[size] = '\0';
336
337 return string;
338}
339
340/* Reads sector SEC_NO from disk D into BUFFER, which must have
341 room for BLOCK_SECTOR_SIZE bytes.
342 Internally synchronizes accesses to disks, so external
343 per-disk locking is unneeded. */
344static void
345ide_read (void *d_, block_sector_t sec_no, void *buffer)
346{
347 struct ata_disk *d = d_;
348 struct channel *c = d->channel;
349 lock_acquire (&c->lock);
350 select_sector (d, sec_no);
351 issue_pio_command (c, CMD_READ_SECTOR_RETRY);
352 sema_down (&c->completion_wait);
353 if (!wait_while_busy (d))
354 PANIC ("%s: disk read failed, sector=%"PRDSNu, d->name, sec_no);
355 input_sector (c, buffer);
356 lock_release (&c->lock);
357}
358
359/* Write sector SEC_NO to disk D from BUFFER, which must contain
360 BLOCK_SECTOR_SIZE bytes. Returns after the disk has
361 acknowledged receiving the data.
362 Internally synchronizes accesses to disks, so external
363 per-disk locking is unneeded. */
364static void
365ide_write (void *d_, block_sector_t sec_no, const void *buffer)
366{
367 struct ata_disk *d = d_;
368 struct channel *c = d->channel;
369 lock_acquire (&c->lock);
370 select_sector (d, sec_no);
371 issue_pio_command (c, CMD_WRITE_SECTOR_RETRY);
372 if (!wait_while_busy (d))
373 PANIC ("%s: disk write failed, sector=%"PRDSNu, d->name, sec_no);
374 output_sector (c, buffer);
375 sema_down (&c->completion_wait);
376 lock_release (&c->lock);
377}
378
379static struct block_operations ide_operations =
380 {
381 ide_read,
382 ide_write
383 };
384
385/* Selects device D, waiting for it to become ready, and then
386 writes SEC_NO to the disk's sector selection registers. (We
387 use LBA mode.) */
388static void
389select_sector (struct ata_disk *d, block_sector_t sec_no)
390{
391 struct channel *c = d->channel;
392
393 ASSERT (sec_no < (1UL << 28));
394
395 select_device_wait (d);
396 outb (reg_nsect (c), 1);
397 outb (reg_lbal (c), sec_no);
398 outb (reg_lbam (c), sec_no >> 8);
399 outb (reg_lbah (c), (sec_no >> 16));
400 outb (reg_device (c),
401 DEV_MBS | DEV_LBA | (d->dev_no == 1 ? DEV_DEV : 0) | (sec_no >> 24));
402}
403
404/* Writes COMMAND to channel C and prepares for receiving a
405 completion interrupt. */
406static void
407issue_pio_command (struct channel *c, uint8_t command)
408{
409 /* Interrupts must be enabled or our semaphore will never be
410 up'd by the completion handler. */
411 ASSERT (intr_get_level () == INTR_ON);
412
413 c->expecting_interrupt = true;
414 outb (reg_command (c), command);
415}
416
417/* Reads a sector from channel C's data register in PIO mode into
418 SECTOR, which must have room for BLOCK_SECTOR_SIZE bytes. */
419static void
420input_sector (struct channel *c, void *sector)
421{
422 insw (reg_data (c), sector, BLOCK_SECTOR_SIZE / 2);
423}
424
425/* Writes SECTOR to channel C's data register in PIO mode.
426 SECTOR must contain BLOCK_SECTOR_SIZE bytes. */
427static void
428output_sector (struct channel *c, const void *sector)
429{
430 outsw (reg_data (c), sector, BLOCK_SECTOR_SIZE / 2);
431}
432
433/* Low-level ATA primitives. */
434
435/* Wait up to 10 seconds for the controller to become idle, that
436 is, for the BSY and DRQ bits to clear in the status register.
437
438 As a side effect, reading the status register clears any
439 pending interrupt. */
440static void
441wait_until_idle (const struct ata_disk *d)
442{
443 int i;
444
445 for (i = 0; i < 1000; i++)
446 {
447 if ((inb (reg_status (d->channel)) & (STA_BSY | STA_DRQ)) == 0)
448 return;
449 timer_usleep (10);
450 }
451
452 printf ("%s: idle timeout\n", d->name);
453}
454
455/* Wait up to 30 seconds for disk D to clear BSY,
456 and then return the status of the DRQ bit.
457 The ATA standards say that a disk may take as long as that to
458 complete its reset. */
459static bool
460wait_while_busy (const struct ata_disk *d)
461{
462 struct channel *c = d->channel;
463 int i;
464
465 for (i = 0; i < 3000; i++)
466 {
467 if (i == 700)
468 printf ("%s: busy, waiting...", d->name);
469 if (!(inb (reg_alt_status (c)) & STA_BSY))
470 {
471 if (i >= 700)
472 printf ("ok\n");
473 return (inb (reg_alt_status (c)) & STA_DRQ) != 0;
474 }
475 timer_msleep (10);
476 }
477
478 printf ("failed\n");
479 return false;
480}
481
482/* Program D's channel so that D is now the selected disk. */
483static void
484select_device (const struct ata_disk *d)
485{
486 struct channel *c = d->channel;
487 uint8_t dev = DEV_MBS;
488 if (d->dev_no == 1)
489 dev |= DEV_DEV;
490 outb (reg_device (c), dev);
491 inb (reg_alt_status (c));
492 timer_nsleep (400);
493}
494
495/* Select disk D in its channel, as select_device(), but wait for
496 the channel to become idle before and after. */
497static void
498select_device_wait (const struct ata_disk *d)
499{
500 wait_until_idle (d);
501 select_device (d);
502 wait_until_idle (d);
503}
504
505/* ATA interrupt handler. */
506static void
507interrupt_handler (struct intr_frame *f)
508{
509 struct channel *c;
510
511 for (c = channels; c < channels + CHANNEL_CNT; c++)
512 if (f->vec_no == c->irq)
513 {
514 if (c->expecting_interrupt)
515 {
516 inb (reg_status (c)); /* Acknowledge interrupt. */
517 sema_up (&c->completion_wait); /* Wake up waiter. */
518 }
519 else
520 printf ("%s: unexpected interrupt\n", c->name);
521 return;
522 }
523
524 NOT_REACHED ();
525}
526
527
diff --git a/pintos-progos/devices/ide.h b/pintos-progos/devices/ide.h
new file mode 100644
index 0000000..b35da5e
--- /dev/null
+++ b/pintos-progos/devices/ide.h
@@ -0,0 +1,6 @@
1#ifndef DEVICES_IDE_H
2#define DEVICES_IDE_H
3
4void ide_init (void);
5
6#endif /* devices/ide.h */
diff --git a/pintos-progos/devices/input.c b/pintos-progos/devices/input.c
new file mode 100644
index 0000000..4a12160
--- /dev/null
+++ b/pintos-progos/devices/input.c
@@ -0,0 +1,52 @@
1#include "devices/input.h"
2#include <debug.h>
3#include "devices/intq.h"
4#include "devices/serial.h"
5
6/* Stores keys from the keyboard and serial port. */
7static struct intq buffer;
8
9/* Initializes the input buffer. */
10void
11input_init (void)
12{
13 intq_init (&buffer);
14}
15
16/* Adds a key to the input buffer.
17 Interrupts must be off and the buffer must not be full. */
18void
19input_putc (uint8_t key)
20{
21 ASSERT (intr_get_level () == INTR_OFF);
22 ASSERT (!intq_full (&buffer));
23
24 intq_putc (&buffer, key);
25 serial_notify ();
26}
27
28/* Retrieves a key from the input buffer.
29 If the buffer is empty, waits for a key to be pressed. */
30uint8_t
31input_getc (void)
32{
33 enum intr_level old_level;
34 uint8_t key;
35
36 old_level = intr_disable ();
37 key = intq_getc (&buffer);
38 serial_notify ();
39 intr_set_level (old_level);
40
41 return key;
42}
43
44/* Returns true if the input buffer is full,
45 false otherwise.
46 Interrupts must be off. */
47bool
48input_full (void)
49{
50 ASSERT (intr_get_level () == INTR_OFF);
51 return intq_full (&buffer);
52}
diff --git a/pintos-progos/devices/input.h b/pintos-progos/devices/input.h
new file mode 100644
index 0000000..a2f50e9
--- /dev/null
+++ b/pintos-progos/devices/input.h
@@ -0,0 +1,12 @@
1#ifndef DEVICES_INPUT_H
2#define DEVICES_INPUT_H
3
4#include <stdbool.h>
5#include <stdint.h>
6
7void input_init (void);
8void input_putc (uint8_t);
9uint8_t input_getc (void);
10bool input_full (void);
11
12#endif /* devices/input.h */
diff --git a/pintos-progos/devices/intq.c b/pintos-progos/devices/intq.c
new file mode 100644
index 0000000..40b23ae
--- /dev/null
+++ b/pintos-progos/devices/intq.c
@@ -0,0 +1,114 @@
1#include "devices/intq.h"
2#include <debug.h>
3#include "threads/thread.h"
4
5static int next (int pos);
6static void wait (struct intq *q, struct thread **waiter);
7static void signal (struct intq *q, struct thread **waiter);
8
9/* Initializes interrupt queue Q. */
10void
11intq_init (struct intq *q)
12{
13 lock_init (&q->lock);
14 q->not_full = q->not_empty = NULL;
15 q->head = q->tail = 0;
16}
17
18/* Returns true if Q is empty, false otherwise. */
19bool
20intq_empty (const struct intq *q)
21{
22 ASSERT (intr_get_level () == INTR_OFF);
23 return q->head == q->tail;
24}
25
26/* Returns true if Q is full, false otherwise. */
27bool
28intq_full (const struct intq *q)
29{
30 ASSERT (intr_get_level () == INTR_OFF);
31 return next (q->head) == q->tail;
32}
33
34/* Removes a byte from Q and returns it.
35 If Q is empty, sleeps until a byte is added.
36 When called from an interrupt handler, Q must not be empty. */
37uint8_t
38intq_getc (struct intq *q)
39{
40 uint8_t byte;
41
42 ASSERT (intr_get_level () == INTR_OFF);
43 while (intq_empty (q))
44 {
45 ASSERT (!intr_context ());
46 lock_acquire (&q->lock);
47 wait (q, &q->not_empty);
48 lock_release (&q->lock);
49 }
50
51 byte = q->buf[q->tail];
52 q->tail = next (q->tail);
53 signal (q, &q->not_full);
54 return byte;
55}
56
57/* Adds BYTE to the end of Q.
58 If Q is full, sleeps until a byte is removed.
59 When called from an interrupt handler, Q must not be full. */
60void
61intq_putc (struct intq *q, uint8_t byte)
62{
63 ASSERT (intr_get_level () == INTR_OFF);
64 while (intq_full (q))
65 {
66 ASSERT (!intr_context ());
67 lock_acquire (&q->lock);
68 wait (q, &q->not_full);
69 lock_release (&q->lock);
70 }
71
72 q->buf[q->head] = byte;
73 q->head = next (q->head);
74 signal (q, &q->not_empty);
75}
76
77/* Returns the position after POS within an intq. */
78static int
79next (int pos)
80{
81 return (pos + 1) % INTQ_BUFSIZE;
82}
83
84/* WAITER must be the address of Q's not_empty or not_full
85 member. Waits until the given condition is true. */
86static void
87wait (struct intq *q UNUSED, struct thread **waiter)
88{
89 ASSERT (!intr_context ());
90 ASSERT (intr_get_level () == INTR_OFF);
91 ASSERT ((waiter == &q->not_empty && intq_empty (q))
92 || (waiter == &q->not_full && intq_full (q)));
93
94 *waiter = thread_current ();
95 thread_block ();
96}
97
98/* WAITER must be the address of Q's not_empty or not_full
99 member, and the associated condition must be true. If a
100 thread is waiting for the condition, wakes it up and resets
101 the waiting thread. */
102static void
103signal (struct intq *q UNUSED, struct thread **waiter)
104{
105 ASSERT (intr_get_level () == INTR_OFF);
106 ASSERT ((waiter == &q->not_empty && !intq_empty (q))
107 || (waiter == &q->not_full && !intq_full (q)));
108
109 if (*waiter != NULL)
110 {
111 thread_unblock (*waiter);
112 *waiter = NULL;
113 }
114}
diff --git a/pintos-progos/devices/intq.h b/pintos-progos/devices/intq.h
new file mode 100644
index 0000000..2312b12
--- /dev/null
+++ b/pintos-progos/devices/intq.h
@@ -0,0 +1,43 @@
1#ifndef DEVICES_INTQ_H
2#define DEVICES_INTQ_H
3
4#include "threads/interrupt.h"
5#include "threads/synch.h"
6
7/* An "interrupt queue", a circular buffer shared between
8 kernel threads and external interrupt handlers.
9
10 Interrupt queue functions can be called from kernel threads or
11 from external interrupt handlers. Except for intq_init(),
12 interrupts must be off in either case.
13
14 The interrupt queue has the structure of a "monitor". Locks
15 and condition variables from threads/synch.h cannot be used in
16 this case, as they normally would, because they can only
17 protect kernel threads from one another, not from interrupt
18 handlers. */
19
20/* Queue buffer size, in bytes. */
21#define INTQ_BUFSIZE 64
22
23/* A circular queue of bytes. */
24struct intq
25 {
26 /* Waiting threads. */
27 struct lock lock; /* Only one thread may wait at once. */
28 struct thread *not_full; /* Thread waiting for not-full condition. */
29 struct thread *not_empty; /* Thread waiting for not-empty condition. */
30
31 /* Queue. */
32 uint8_t buf[INTQ_BUFSIZE]; /* Buffer. */
33 int head; /* New data is written here. */
34 int tail; /* Old data is read here. */
35 };
36
37void intq_init (struct intq *);
38bool intq_empty (const struct intq *);
39bool intq_full (const struct intq *);
40uint8_t intq_getc (struct intq *);
41void intq_putc (struct intq *, uint8_t);
42
43#endif /* devices/intq.h */
diff --git a/pintos-progos/devices/kbd.c b/pintos-progos/devices/kbd.c
new file mode 100644
index 0000000..fcc82be
--- /dev/null
+++ b/pintos-progos/devices/kbd.c
@@ -0,0 +1,213 @@
1#include "devices/kbd.h"
2#include <ctype.h>
3#include <debug.h>
4#include <stdio.h>
5#include <string.h>
6#include "devices/input.h"
7#include "devices/shutdown.h"
8#include "threads/interrupt.h"
9#include "threads/io.h"
10
11/* Keyboard data register port. */
12#define DATA_REG 0x60
13
14/* Current state of shift keys.
15 True if depressed, false otherwise. */
16static bool left_shift, right_shift; /* Left and right Shift keys. */
17static bool left_alt, right_alt; /* Left and right Alt keys. */
18static bool left_ctrl, right_ctrl; /* Left and right Ctl keys. */
19
20/* Status of Caps Lock.
21 True when on, false when off. */
22static bool caps_lock;
23
24/* Number of keys pressed. */
25static int64_t key_cnt;
26
27static intr_handler_func keyboard_interrupt;
28
29/* Initializes the keyboard. */
30void
31kbd_init (void)
32{
33 intr_register_ext (0x21, keyboard_interrupt, "8042 Keyboard");
34}
35
36/* Prints keyboard statistics. */
37void
38kbd_print_stats (void)
39{
40 printf ("Keyboard: %lld keys pressed\n", key_cnt);
41}
42
43/* Maps a set of contiguous scancodes into characters. */
44struct keymap
45 {
46 uint8_t first_scancode; /* First scancode. */
47 const char *chars; /* chars[0] has scancode first_scancode,
48 chars[1] has scancode first_scancode + 1,
49 and so on to the end of the string. */
50 };
51
52/* Keys that produce the same characters regardless of whether
53 the Shift keys are down. Case of letters is an exception
54 that we handle elsewhere. */
55static const struct keymap invariant_keymap[] =
56 {
57 {0x01, "\033"}, /* Escape. */
58 {0x0e, "\b"},
59 {0x0f, "\tQWERTYUIOP"},
60 {0x1c, "\r"},
61 {0x1e, "ASDFGHJKL"},
62 {0x2c, "ZXCVBNM"},
63 {0x37, "*"},
64 {0x39, " "},
65 {0x53, "\177"}, /* Delete. */
66 {0, NULL},
67 };
68
69/* Characters for keys pressed without Shift, for those keys
70 where it matters. */
71static const struct keymap unshifted_keymap[] =
72 {
73 {0x02, "1234567890-="},
74 {0x1a, "[]"},
75 {0x27, ";'`"},
76 {0x2b, "\\"},
77 {0x33, ",./"},
78 {0, NULL},
79 };
80
81/* Characters for keys pressed with Shift, for those keys where
82 it matters. */
83static const struct keymap shifted_keymap[] =
84 {
85 {0x02, "!@#$%^&*()_+"},
86 {0x1a, "{}"},
87 {0x27, ":\"~"},
88 {0x2b, "|"},
89 {0x33, "<>?"},
90 {0, NULL},
91 };
92
93static bool map_key (const struct keymap[], unsigned scancode, uint8_t *);
94
95static void
96keyboard_interrupt (struct intr_frame *args UNUSED)
97{
98 /* Status of shift keys. */
99 bool shift = left_shift || right_shift;
100 bool alt = left_alt || right_alt;
101 bool ctrl = left_ctrl || right_ctrl;
102
103 /* Keyboard scancode. */
104 unsigned code;
105
106 /* False if key pressed, true if key released. */
107 bool release;
108
109 /* Character that corresponds to `code'. */
110 uint8_t c;
111
112 /* Read scancode, including second byte if prefix code. */
113 code = inb (DATA_REG);
114 if (code == 0xe0)
115 code = (code << 8) | inb (DATA_REG);
116
117 /* Bit 0x80 distinguishes key press from key release
118 (even if there's a prefix). */
119 release = (code & 0x80) != 0;
120 code &= ~0x80u;
121
122 /* Interpret key. */
123 if (code == 0x3a)
124 {
125 /* Caps Lock. */
126 if (!release)
127 caps_lock = !caps_lock;
128 }
129 else if (map_key (invariant_keymap, code, &c)
130 || (!shift && map_key (unshifted_keymap, code, &c))
131 || (shift && map_key (shifted_keymap, code, &c)))
132 {
133 /* Ordinary character. */
134 if (!release)
135 {
136 /* Reboot if Ctrl+Alt+Del pressed. */
137 if (c == 0177 && ctrl && alt)
138 shutdown_reboot ();
139
140 /* Handle Ctrl, Shift.
141 Note that Ctrl overrides Shift. */
142 if (ctrl && c >= 0x40 && c < 0x60)
143 {
144 /* A is 0x41, Ctrl+A is 0x01, etc. */
145 c -= 0x40;
146 }
147 else if (shift == caps_lock)
148 c = tolower (c);
149
150 /* Handle Alt by setting the high bit.
151 This 0x80 is unrelated to the one used to
152 distinguish key press from key release. */
153 if (alt)
154 c += 0x80;
155
156 /* Append to keyboard buffer. */
157 if (!input_full ())
158 {
159 key_cnt++;
160 input_putc (c);
161 }
162 }
163 }
164 else
165 {
166 /* Maps a keycode into a shift state variable. */
167 struct shift_key
168 {
169 unsigned scancode;
170 bool *state_var;
171 };
172
173 /* Table of shift keys. */
174 static const struct shift_key shift_keys[] =
175 {
176 { 0x2a, &left_shift},
177 { 0x36, &right_shift},
178 { 0x38, &left_alt},
179 {0xe038, &right_alt},
180 { 0x1d, &left_ctrl},
181 {0xe01d, &right_ctrl},
182 {0, NULL},
183 };
184
185 const struct shift_key *key;
186
187 /* Scan the table. */
188 for (key = shift_keys; key->scancode != 0; key++)
189 if (key->scancode == code)
190 {
191 *key->state_var = !release;
192 break;
193 }
194 }
195}
196
197/* Scans the array of keymaps K for SCANCODE.
198 If found, sets *C to the corresponding character and returns
199 true.
200 If not found, returns false and C is ignored. */
201static bool
202map_key (const struct keymap k[], unsigned scancode, uint8_t *c)
203{
204 for (; k->first_scancode != 0; k++)
205 if (scancode >= k->first_scancode
206 && scancode < k->first_scancode + strlen (k->chars))
207 {
208 *c = k->chars[scancode - k->first_scancode];
209 return true;
210 }
211
212 return false;
213}
diff --git a/pintos-progos/devices/kbd.h b/pintos-progos/devices/kbd.h
new file mode 100644
index 0000000..ed9c06b
--- /dev/null
+++ b/pintos-progos/devices/kbd.h
@@ -0,0 +1,9 @@
1#ifndef DEVICES_KBD_H
2#define DEVICES_KBD_H
3
4#include <stdint.h>
5
6void kbd_init (void);
7void kbd_print_stats (void);
8
9#endif /* devices/kbd.h */
diff --git a/pintos-progos/devices/partition.c b/pintos-progos/devices/partition.c
new file mode 100644
index 0000000..7e97332
--- /dev/null
+++ b/pintos-progos/devices/partition.c
@@ -0,0 +1,324 @@
1#include "devices/partition.h"
2#include <packed.h>
3#include <stdlib.h>
4#include <string.h>
5#include <stdio.h>
6#include "devices/block.h"
7#include "threads/malloc.h"
8
9/* A partition of a block device. */
10struct partition
11 {
12 struct block *block; /* Underlying block device. */
13 block_sector_t start; /* First sector within device. */
14 };
15
16static struct block_operations partition_operations;
17
18static void read_partition_table (struct block *, block_sector_t sector,
19 block_sector_t primary_extended_sector,
20 int *part_nr);
21static void found_partition (struct block *, uint8_t type,
22 block_sector_t start, block_sector_t size,
23 int part_nr);
24static const char *partition_type_name (uint8_t);
25
26/* Scans BLOCK for partitions of interest to Pintos. */
27void
28partition_scan (struct block *block)
29{
30 int part_nr = 0;
31 read_partition_table (block, 0, 0, &part_nr);
32 if (part_nr == 0)
33 printf ("%s: Device contains no partitions\n", block_name (block));
34}
35
36/* Reads the partition table in the given SECTOR of BLOCK and
37 scans it for partitions of interest to Pintos.
38
39 If SECTOR is 0, so that this is the top-level partition table
40 on BLOCK, then PRIMARY_EXTENDED_SECTOR is not meaningful;
41 otherwise, it should designate the sector of the top-level
42 extended partition table that was traversed to arrive at
43 SECTOR, for use in finding logical partitions (see the large
44 comment below).
45
46 PART_NR points to the number of non-empty primary or logical
47 partitions already encountered on BLOCK. It is incremented as
48 partitions are found. */
49static void
50read_partition_table (struct block *block, block_sector_t sector,
51 block_sector_t primary_extended_sector,
52 int *part_nr)
53{
54 /* Format of a partition table entry. See [Partitions]. */
55 struct partition_table_entry
56 {
57 uint8_t bootable; /* 0x00=not bootable, 0x80=bootable. */
58 uint8_t start_chs[3]; /* Encoded starting cylinder, head, sector. */
59 uint8_t type; /* Partition type (see partition_type_name). */
60 uint8_t end_chs[3]; /* Encoded ending cylinder, head, sector. */
61 uint32_t offset; /* Start sector offset from partition table. */
62 uint32_t size; /* Number of sectors. */
63 }
64 PACKED;
65
66 /* Partition table sector. */
67 struct partition_table
68 {
69 uint8_t loader[446]; /* Loader, in top-level partition table. */
70 struct partition_table_entry partitions[4]; /* Table entries. */
71 uint16_t signature; /* Should be 0xaa55. */
72 }
73 PACKED;
74
75 struct partition_table *pt;
76 size_t i;
77
78 /* Check SECTOR validity. */
79 if (sector >= block_size (block))
80 {
81 printf ("%s: Partition table at sector %"PRDSNu" past end of device.\n",
82 block_name (block), sector);
83 return;
84 }
85
86 /* Read sector. */
87 ASSERT (sizeof *pt == BLOCK_SECTOR_SIZE);
88 pt = malloc (sizeof *pt);
89 if (pt == NULL)
90 PANIC ("Failed to allocate memory for partition table.");
91 block_read (block, 0, pt);
92
93 /* Check signature. */
94 if (pt->signature != 0xaa55)
95 {
96 if (primary_extended_sector == 0)
97 printf ("%s: Invalid partition table signature\n", block_name (block));
98 else
99 printf ("%s: Invalid extended partition table in sector %"PRDSNu"\n",
100 block_name (block), sector);
101 free (pt);
102 return;
103 }
104
105 /* Parse partitions. */
106 for (i = 0; i < sizeof pt->partitions / sizeof *pt->partitions; i++)
107 {
108 struct partition_table_entry *e = &pt->partitions[i];
109
110 if (e->size == 0 || e->type == 0)
111 {
112 /* Ignore empty partition. */
113 }
114 else if (e->type == 0x05 /* Extended partition. */
115 || e->type == 0x0f /* Windows 98 extended partition. */
116 || e->type == 0x85 /* Linux extended partition. */
117 || e->type == 0xc5) /* DR-DOS extended partition. */
118 {
119 printf ("%s: Extended partition in sector %"PRDSNu"\n",
120 block_name (block), sector);
121
122 /* The interpretation of the offset field for extended
123 partitions is bizarre. When the extended partition
124 table entry is in the master boot record, that is,
125 the device's primary partition table in sector 0, then
126 the offset is an absolute sector number. Otherwise,
127 no matter how deep the partition table we're reading
128 is nested, the offset is relative to the start of
129 the extended partition that the MBR points to. */
130 if (sector == 0)
131 read_partition_table (block, e->offset, e->offset, part_nr);
132 else
133 read_partition_table (block, e->offset + primary_extended_sector,
134 primary_extended_sector, part_nr);
135 }
136 else
137 {
138 ++*part_nr;
139
140 found_partition (block, e->type, e->offset + sector,
141 e->size, *part_nr);
142 }
143 }
144
145 free (pt);
146}
147
148/* We have found a primary or logical partition of the given TYPE
149 on BLOCK, starting at sector START and continuing for SIZE
150 sectors, which we are giving the partition number PART_NR.
151 Check whether this is a partition of interest to Pintos, and
152 if so then add it to the proper element of partitions[]. */
153static void
154found_partition (struct block *block, uint8_t part_type,
155 block_sector_t start, block_sector_t size,
156 int part_nr)
157{
158 if (start >= block_size (block))
159 printf ("%s%d: Partition starts past end of device (sector %"PRDSNu")\n",
160 block_name (block), part_nr, start);
161 else if (start + size < start || start + size > block_size (block))
162 printf ("%s%d: Partition end (%"PRDSNu") past end of device (%"PRDSNu")\n",
163 block_name (block), part_nr, start + size, block_size (block));
164 else
165 {
166 enum block_type type = (part_type == 0x20 ? BLOCK_KERNEL
167 : part_type == 0x21 ? BLOCK_FILESYS
168 : part_type == 0x22 ? BLOCK_SCRATCH
169 : part_type == 0x23 ? BLOCK_SWAP
170 : BLOCK_FOREIGN);
171 struct partition *p;
172 char extra_info[128];
173 char name[16];
174
175 p = malloc (sizeof *p);
176 if (p == NULL)
177 PANIC ("Failed to allocate memory for partition descriptor");
178 p->block = block;
179 p->start = start;
180
181 snprintf (name, sizeof name, "%s%d", block_name (block), part_nr);
182 snprintf (extra_info, sizeof extra_info, "%s (%02x)",
183 partition_type_name (part_type), part_type);
184 block_register (name, type, extra_info, size, &partition_operations, p);
185 }
186}
187
188/* Returns a human-readable name for the given partition TYPE. */
189static const char *
190partition_type_name (uint8_t type)
191{
192 /* Name of each known type of partition.
193 From util-linux-2.12r/fdisk/i386_sys_types.c.
194 This initializer makes use of a C99 feature that allows
195 array elements to be initialized by index. */
196 static const char *type_names[256] =
197 {
198 [0x00] = "Empty",
199 [0x01] = "FAT12",
200 [0x02] = "XENIX root",
201 [0x03] = "XENIX usr",
202 [0x04] = "FAT16 <32M",
203 [0x05] = "Extended",
204 [0x06] = "FAT16",
205 [0x07] = "HPFS/NTFS",
206 [0x08] = "AIX",
207 [0x09] = "AIX bootable",
208 [0x0a] = "OS/2 Boot Manager",
209 [0x0b] = "W95 FAT32",
210 [0x0c] = "W95 FAT32 (LBA)",
211 [0x0e] = "W95 FAT16 (LBA)",
212 [0x0f] = "W95 Ext'd (LBA)",
213 [0x10] = "OPUS",
214 [0x11] = "Hidden FAT12",
215 [0x12] = "Compaq diagnostics",
216 [0x14] = "Hidden FAT16 <32M",
217 [0x16] = "Hidden FAT16",
218 [0x17] = "Hidden HPFS/NTFS",
219 [0x18] = "AST SmartSleep",
220 [0x1b] = "Hidden W95 FAT32",
221 [0x1c] = "Hidden W95 FAT32 (LBA)",
222 [0x1e] = "Hidden W95 FAT16 (LBA)",
223 [0x20] = "Pintos OS kernel",
224 [0x21] = "Pintos file system",
225 [0x22] = "Pintos scratch",
226 [0x23] = "Pintos swap",
227 [0x24] = "NEC DOS",
228 [0x39] = "Plan 9",
229 [0x3c] = "PartitionMagic recovery",
230 [0x40] = "Venix 80286",
231 [0x41] = "PPC PReP Boot",
232 [0x42] = "SFS",
233 [0x4d] = "QNX4.x",
234 [0x4e] = "QNX4.x 2nd part",
235 [0x4f] = "QNX4.x 3rd part",
236 [0x50] = "OnTrack DM",
237 [0x51] = "OnTrack DM6 Aux1",
238 [0x52] = "CP/M",
239 [0x53] = "OnTrack DM6 Aux3",
240 [0x54] = "OnTrackDM6",
241 [0x55] = "EZ-Drive",
242 [0x56] = "Golden Bow",
243 [0x5c] = "Priam Edisk",
244 [0x61] = "SpeedStor",
245 [0x63] = "GNU HURD or SysV",
246 [0x64] = "Novell Netware 286",
247 [0x65] = "Novell Netware 386",
248 [0x70] = "DiskSecure Multi-Boot",
249 [0x75] = "PC/IX",
250 [0x80] = "Old Minix",
251 [0x81] = "Minix / old Linux",
252 [0x82] = "Linux swap / Solaris",
253 [0x83] = "Linux",
254 [0x84] = "OS/2 hidden C: drive",
255 [0x85] = "Linux extended",
256 [0x86] = "NTFS volume set",
257 [0x87] = "NTFS volume set",
258 [0x88] = "Linux plaintext",
259 [0x8e] = "Linux LVM",
260 [0x93] = "Amoeba",
261 [0x94] = "Amoeba BBT",
262 [0x9f] = "BSD/OS",
263 [0xa0] = "IBM Thinkpad hibernation",
264 [0xa5] = "FreeBSD",
265 [0xa6] = "OpenBSD",
266 [0xa7] = "NeXTSTEP",
267 [0xa8] = "Darwin UFS",
268 [0xa9] = "NetBSD",
269 [0xab] = "Darwin boot",
270 [0xb7] = "BSDI fs",
271 [0xb8] = "BSDI swap",
272 [0xbb] = "Boot Wizard hidden",
273 [0xbe] = "Solaris boot",
274 [0xbf] = "Solaris",
275 [0xc1] = "DRDOS/sec (FAT-12)",
276 [0xc4] = "DRDOS/sec (FAT-16 < 32M)",
277 [0xc6] = "DRDOS/sec (FAT-16)",
278 [0xc7] = "Syrinx",
279 [0xda] = "Non-FS data",
280 [0xdb] = "CP/M / CTOS / ...",
281 [0xde] = "Dell Utility",
282 [0xdf] = "BootIt",
283 [0xe1] = "DOS access",
284 [0xe3] = "DOS R/O",
285 [0xe4] = "SpeedStor",
286 [0xeb] = "BeOS fs",
287 [0xee] = "EFI GPT",
288 [0xef] = "EFI (FAT-12/16/32)",
289 [0xf0] = "Linux/PA-RISC boot",
290 [0xf1] = "SpeedStor",
291 [0xf4] = "SpeedStor",
292 [0xf2] = "DOS secondary",
293 [0xfd] = "Linux raid autodetect",
294 [0xfe] = "LANstep",
295 [0xff] = "BBT",
296 };
297
298 return type_names[type] != NULL ? type_names[type] : "Unknown";
299}
300
301/* Reads sector SECTOR from partition P into BUFFER, which must
302 have room for BLOCK_SECTOR_SIZE bytes. */
303static void
304partition_read (void *p_, block_sector_t sector, void *buffer)
305{
306 struct partition *p = p_;
307 block_read (p->block, p->start + sector, buffer);
308}
309
310/* Write sector SECTOR to partition P from BUFFER, which must
311 contain BLOCK_SECTOR_SIZE bytes. Returns after the block has
312 acknowledged receiving the data. */
313static void
314partition_write (void *p_, block_sector_t sector, const void *buffer)
315{
316 struct partition *p = p_;
317 block_write (p->block, p->start + sector, buffer);
318}
319
320static struct block_operations partition_operations =
321 {
322 partition_read,
323 partition_write
324 };
diff --git a/pintos-progos/devices/partition.h b/pintos-progos/devices/partition.h
new file mode 100644
index 0000000..47fea4d
--- /dev/null
+++ b/pintos-progos/devices/partition.h
@@ -0,0 +1,8 @@
1#ifndef DEVICES_PARTITION_H
2#define DEVICES_PARTITION_H
3
4struct block;
5
6void partition_scan (struct block *);
7
8#endif /* devices/partition.h */
diff --git a/pintos-progos/devices/pit.c b/pintos-progos/devices/pit.c
new file mode 100644
index 0000000..bfb1889
--- /dev/null
+++ b/pintos-progos/devices/pit.c
@@ -0,0 +1,83 @@
1#include "devices/pit.h"
2#include <debug.h>
3#include <stdint.h>
4#include "threads/interrupt.h"
5#include "threads/io.h"
6
7/* Interface to 8254 Programmable Interrupt Timer (PIT).
8 Refer to [8254] for details. */
9
10/* 8254 registers. */
11#define PIT_PORT_CONTROL 0x43 /* Control port. */
12#define PIT_PORT_COUNTER(CHANNEL) (0x40 + (CHANNEL)) /* Counter port. */
13
14/* PIT cycles per second. */
15#define PIT_HZ 1193180
16
17/* Configure the given CHANNEL in the PIT. In a PC, the PIT's
18 three output channels are hooked up like this:
19
20 - Channel 0 is connected to interrupt line 0, so that it can
21 be used as a periodic timer interrupt, as implemented in
22 Pintos in devices/timer.c.
23
24 - Channel 1 is used for dynamic RAM refresh (in older PCs).
25 No good can come of messing with this.
26
27 - Channel 2 is connected to the PC speaker, so that it can
28 be used to play a tone, as implemented in Pintos in
29 devices/speaker.c.
30
31 MODE specifies the form of output:
32
33 - Mode 2 is a periodic pulse: the channel's output is 1 for
34 most of the period, but drops to 0 briefly toward the end
35 of the period. This is useful for hooking up to an
36 interrupt controller to generate a periodic interrupt.
37
38 - Mode 3 is a square wave: for the first half of the period
39 it is 1, for the second half it is 0. This is useful for
40 generating a tone on a speaker.
41
42 - Other modes are less useful.
43
44 FREQUENCY is the number of periods per second, in Hz. */
45void
46pit_configure_channel (int channel, int mode, int frequency)
47{
48 uint16_t count;
49 enum intr_level old_level;
50
51 ASSERT (channel == 0 || channel == 2);
52 ASSERT (mode == 2 || mode == 3);
53
54 /* Convert FREQUENCY to a PIT counter value. The PIT has a
55 clock that runs at PIT_HZ cycles per second. We must
56 translate FREQUENCY into a number of these cycles. */
57 if (frequency < 19)
58 {
59 /* Frequency is too low: the quotient would overflow the
60 16-bit counter. Force it to 0, which the PIT treats as
61 65536, the highest possible count. This yields a 18.2
62 Hz timer, approximately. */
63 count = 0;
64 }
65 else if (frequency > PIT_HZ)
66 {
67 /* Frequency is too high: the quotient would underflow to
68 0, which the PIT would interpret as 65536. A count of 1
69 is illegal in mode 2, so we force it to 2, which yields
70 a 596.590 kHz timer, approximately. (This timer rate is
71 probably too fast to be useful anyhow.) */
72 count = 2;
73 }
74 else
75 count = (PIT_HZ + frequency / 2) / frequency;
76
77 /* Configure the PIT mode and load its counters. */
78 old_level = intr_disable ();
79 outb (PIT_PORT_CONTROL, (channel << 6) | 0x30 | (mode << 1));
80 outb (PIT_PORT_COUNTER (channel), count);
81 outb (PIT_PORT_COUNTER (channel), count >> 8);
82 intr_set_level (old_level);
83}
diff --git a/pintos-progos/devices/pit.h b/pintos-progos/devices/pit.h
new file mode 100644
index 0000000..dff36ae
--- /dev/null
+++ b/pintos-progos/devices/pit.h
@@ -0,0 +1,8 @@
1#ifndef DEVICES_PIT_H
2#define DEVICES_PIT_H
3
4#include <stdint.h>
5
6void pit_configure_channel (int channel, int mode, int frequency);
7
8#endif /* devices/pit.h */
diff --git a/pintos-progos/devices/rtc.c b/pintos-progos/devices/rtc.c
new file mode 100644
index 0000000..d99eb46
--- /dev/null
+++ b/pintos-progos/devices/rtc.c
@@ -0,0 +1,112 @@
1#include "devices/rtc.h"
2#include <stdio.h>
3#include "threads/io.h"
4
5/* This code is an interface to the MC146818A-compatible real
6 time clock found on PC motherboards. See [MC146818A] for
7 hardware details. */
8
9/* I/O register addresses. */
10#define CMOS_REG_SET 0x70 /* Selects CMOS register exposed by REG_IO. */
11#define CMOS_REG_IO 0x71 /* Contains the selected data byte. */
12
13/* Indexes of CMOS registers with real-time clock functions.
14 Note that all of these registers are in BCD format,
15 so that 0x59 means 59, not 89. */
16#define RTC_REG_SEC 0 /* Second: 0x00...0x59. */
17#define RTC_REG_MIN 2 /* Minute: 0x00...0x59. */
18#define RTC_REG_HOUR 4 /* Hour: 0x00...0x23. */
19#define RTC_REG_MDAY 7 /* Day of the month: 0x01...0x31. */
20#define RTC_REG_MON 8 /* Month: 0x01...0x12. */
21#define RTC_REG_YEAR 9 /* Year: 0x00...0x99. */
22
23/* Indexes of CMOS control registers. */
24#define RTC_REG_A 0x0a /* Register A: update-in-progress. */
25#define RTC_REG_B 0x0b /* Register B: 24/12 hour time, irq enables. */
26#define RTC_REG_C 0x0c /* Register C: pending interrupts. */
27#define RTC_REG_D 0x0d /* Register D: valid time? */
28
29/* Register A. */
30#define RTCSA_UIP 0x80 /* Set while time update in progress. */
31
32/* Register B. */
33#define RTCSB_SET 0x80 /* Disables update to let time be set. */
34#define RTCSB_DM 0x04 /* 0 = BCD time format, 1 = binary format. */
35#define RTCSB_24HR 0x02 /* 0 = 12-hour format, 1 = 24-hour format. */
36
37static int bcd_to_bin (uint8_t);
38static uint8_t cmos_read (uint8_t index);
39
40/* Returns number of seconds since Unix epoch of January 1,
41 1970. */
42time_t
43rtc_get_time (void)
44{
45 static const int days_per_month[12] =
46 {
47 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
48 };
49 int sec, min, hour, mday, mon, year;
50 time_t time;
51 int i;
52
53 /* Get time components.
54
55 We repeatedly read the time until it is stable from one read
56 to another, in case we start our initial read in the middle
57 of an update. This strategy is not recommended by the
58 MC146818A datasheet, but it is simpler than any of their
59 suggestions and, furthermore, it is also used by Linux.
60
61 The MC146818A can be configured for BCD or binary format,
62 but for historical reasons everyone always uses BCD format
63 except on obscure non-PC platforms, so we don't bother
64 trying to detect the format in use. */
65 do
66 {
67 sec = bcd_to_bin (cmos_read (RTC_REG_SEC));
68 min = bcd_to_bin (cmos_read (RTC_REG_MIN));
69 hour = bcd_to_bin (cmos_read (RTC_REG_HOUR));
70 mday = bcd_to_bin (cmos_read (RTC_REG_MDAY));
71 mon = bcd_to_bin (cmos_read (RTC_REG_MON));
72 year = bcd_to_bin (cmos_read (RTC_REG_YEAR));
73 }
74 while (sec != bcd_to_bin (cmos_read (RTC_REG_SEC)));
75
76 /* Translate years-since-1900 into years-since-1970.
77 If it's before the epoch, assume that it has passed 2000.
78 This will break at 2070, but that's long after our 31-bit
79 time_t breaks in 2038. */
80 if (year < 70)
81 year += 100;
82 year -= 70;
83
84 /* Break down all components into seconds. */
85 time = (year * 365 + (year - 1) / 4) * 24 * 60 * 60;
86 for (i = 1; i <= mon; i++)
87 time += days_per_month[i - 1] * 24 * 60 * 60;
88 if (mon > 2 && year % 4 == 0)
89 time += 24 * 60 * 60;
90 time += (mday - 1) * 24 * 60 * 60;
91 time += hour * 60 * 60;
92 time += min * 60;
93 time += sec;
94
95 return time;
96}
97
98/* Returns the integer value of the given BCD byte. */
99static int
100bcd_to_bin (uint8_t x)
101{
102 return (x & 0x0f) + ((x >> 4) * 10);
103}
104
105/* Reads a byte from the CMOS register with the given INDEX and
106 returns the byte read. */
107static uint8_t
108cmos_read (uint8_t index)
109{
110 outb (CMOS_REG_SET, index);
111 return inb (CMOS_REG_IO);
112}
diff --git a/pintos-progos/devices/rtc.h b/pintos-progos/devices/rtc.h
new file mode 100644
index 0000000..96a822f
--- /dev/null
+++ b/pintos-progos/devices/rtc.h
@@ -0,0 +1,8 @@
1#ifndef RTC_H
2#define RTC_H
3
4typedef unsigned long time_t;
5
6time_t rtc_get_time (void);
7
8#endif
diff --git a/pintos-progos/devices/serial.c b/pintos-progos/devices/serial.c
new file mode 100644
index 0000000..df770a7
--- /dev/null
+++ b/pintos-progos/devices/serial.c
@@ -0,0 +1,228 @@
1#include "devices/serial.h"
2#include <debug.h>
3#include "devices/input.h"
4#include "devices/intq.h"
5#include "devices/timer.h"
6#include "threads/io.h"
7#include "threads/interrupt.h"
8#include "threads/synch.h"
9#include "threads/thread.h"
10
11/* Register definitions for the 16550A UART used in PCs.
12 The 16550A has a lot more going on than shown here, but this
13 is all we need.
14
15 Refer to [PC16650D] for hardware information. */
16
17/* I/O port base address for the first serial port. */
18#define IO_BASE 0x3f8
19
20/* DLAB=0 registers. */
21#define RBR_REG (IO_BASE + 0) /* Receiver Buffer Reg. (read-only). */
22#define THR_REG (IO_BASE + 0) /* Transmitter Holding Reg. (write-only). */
23#define IER_REG (IO_BASE + 1) /* Interrupt Enable Reg.. */
24
25/* DLAB=1 registers. */
26#define LS_REG (IO_BASE + 0) /* Divisor Latch (LSB). */
27#define MS_REG (IO_BASE + 1) /* Divisor Latch (MSB). */
28
29/* DLAB-insensitive registers. */
30#define IIR_REG (IO_BASE + 2) /* Interrupt Identification Reg. (read-only) */
31#define FCR_REG (IO_BASE + 2) /* FIFO Control Reg. (write-only). */
32#define LCR_REG (IO_BASE + 3) /* Line Control Register. */
33#define MCR_REG (IO_BASE + 4) /* MODEM Control Register. */
34#define LSR_REG (IO_BASE + 5) /* Line Status Register (read-only). */
35
36/* Interrupt Enable Register bits. */
37#define IER_RECV 0x01 /* Interrupt when data received. */
38#define IER_XMIT 0x02 /* Interrupt when transmit finishes. */
39
40/* Line Control Register bits. */
41#define LCR_N81 0x03 /* No parity, 8 data bits, 1 stop bit. */
42#define LCR_DLAB 0x80 /* Divisor Latch Access Bit (DLAB). */
43
44/* MODEM Control Register. */
45#define MCR_OUT2 0x08 /* Output line 2. */
46
47/* Line Status Register. */
48#define LSR_DR 0x01 /* Data Ready: received data byte is in RBR. */
49#define LSR_THRE 0x20 /* THR Empty. */
50
51/* Transmission mode. */
52static enum { UNINIT, POLL, QUEUE } mode;
53
54/* Data to be transmitted. */
55static struct intq txq;
56
57static void set_serial (int bps);
58static void putc_poll (uint8_t);
59static void write_ier (void);
60static intr_handler_func serial_interrupt;
61
62/* Initializes the serial port device for polling mode.
63 Polling mode busy-waits for the serial port to become free
64 before writing to it. It's slow, but until interrupts have
65 been initialized it's all we can do. */
66static void
67init_poll (void)
68{
69 ASSERT (mode == UNINIT);
70 outb (IER_REG, 0); /* Turn off all interrupts. */
71 outb (FCR_REG, 0); /* Disable FIFO. */
72 set_serial (9600); /* 9.6 kbps, N-8-1. */
73 outb (MCR_REG, MCR_OUT2); /* Required to enable interrupts. */
74 intq_init (&txq);
75 mode = POLL;
76}
77
78/* Initializes the serial port device for queued interrupt-driven
79 I/O. With interrupt-driven I/O we don't waste CPU time
80 waiting for the serial device to become ready. */
81void
82serial_init_queue (void)
83{
84 enum intr_level old_level;
85
86 if (mode == UNINIT)
87 init_poll ();
88 ASSERT (mode == POLL);
89
90 intr_register_ext (0x20 + 4, serial_interrupt, "serial");
91 mode = QUEUE;
92 old_level = intr_disable ();
93 write_ier ();
94 intr_set_level (old_level);
95}
96
97/* Sends BYTE to the serial port. */
98void
99serial_putc (uint8_t byte)
100{
101 enum intr_level old_level = intr_disable ();
102
103 if (mode != QUEUE)
104 {
105 /* If we're not set up for interrupt-driven I/O yet,
106 use dumb polling to transmit a byte. */
107 if (mode == UNINIT)
108 init_poll ();
109 putc_poll (byte);
110 }
111 else
112 {
113 /* Otherwise, queue a byte and update the interrupt enable
114 register. */
115 if (old_level == INTR_OFF && intq_full (&txq))
116 {
117 /* Interrupts are off and the transmit queue is full.
118 If we wanted to wait for the queue to empty,
119 we'd have to reenable interrupts.
120 That's impolite, so we'll send a character via
121 polling instead. */
122 putc_poll (intq_getc (&txq));
123 }
124
125 intq_putc (&txq, byte);
126 write_ier ();
127 }
128
129 intr_set_level (old_level);
130}
131
132/* Flushes anything in the serial buffer out the port in polling
133 mode. */
134void
135serial_flush (void)
136{
137 enum intr_level old_level = intr_disable ();
138 while (!intq_empty (&txq))
139 putc_poll (intq_getc (&txq));
140 intr_set_level (old_level);
141}
142
143/* The fullness of the input buffer may have changed. Reassess
144 whether we should block receive interrupts.
145 Called by the input buffer routines when characters are added
146 to or removed from the buffer. */
147void
148serial_notify (void)
149{
150 ASSERT (intr_get_level () == INTR_OFF);
151 if (mode == QUEUE)
152 write_ier ();
153}
154
155/* Configures the serial port for BPS bits per second. */
156static void
157set_serial (int bps)
158{
159 int base_rate = 1843200 / 16; /* Base rate of 16550A, in Hz. */
160 uint16_t divisor = base_rate / bps; /* Clock rate divisor. */
161
162 ASSERT (bps >= 300 && bps <= 115200);
163
164 /* Enable DLAB. */
165 outb (LCR_REG, LCR_N81 | LCR_DLAB);
166
167 /* Set data rate. */
168 outb (LS_REG, divisor & 0xff);
169 outb (MS_REG, divisor >> 8);
170
171 /* Reset DLAB. */
172 outb (LCR_REG, LCR_N81);
173}
174
175/* Update interrupt enable register. */
176static void
177write_ier (void)
178{
179 uint8_t ier = 0;
180
181 ASSERT (intr_get_level () == INTR_OFF);
182
183 /* Enable transmit interrupt if we have any characters to
184 transmit. */
185 if (!intq_empty (&txq))
186 ier |= IER_XMIT;
187
188 /* Enable receive interrupt if we have room to store any
189 characters we receive. */
190 if (!input_full ())
191 ier |= IER_RECV;
192
193 outb (IER_REG, ier);
194}
195
196/* Polls the serial port until it's ready,
197 and then transmits BYTE. */
198static void
199putc_poll (uint8_t byte)
200{
201 ASSERT (intr_get_level () == INTR_OFF);
202
203 while ((inb (LSR_REG) & LSR_THRE) == 0)
204 continue;
205 outb (THR_REG, byte);
206}
207
208/* Serial interrupt handler. */
209static void
210serial_interrupt (struct intr_frame *f UNUSED)
211{
212 /* Inquire about interrupt in UART. Without this, we can
213 occasionally miss an interrupt running under QEMU. */
214 inb (IIR_REG);
215
216 /* As long as we have room to receive a byte, and the hardware
217 has a byte for us, receive a byte. */
218 while (!input_full () && (inb (LSR_REG) & LSR_DR) != 0)
219 input_putc (inb (RBR_REG));
220
221 /* As long as we have a byte to transmit, and the hardware is
222 ready to accept a byte for transmission, transmit a byte. */
223 while (!intq_empty (&txq) && (inb (LSR_REG) & LSR_THRE) != 0)
224 outb (THR_REG, intq_getc (&txq));
225
226 /* Update interrupt enable register based on queue status. */
227 write_ier ();
228}
diff --git a/pintos-progos/devices/serial.h b/pintos-progos/devices/serial.h
new file mode 100644
index 0000000..6e04778
--- /dev/null
+++ b/pintos-progos/devices/serial.h
@@ -0,0 +1,11 @@
1#ifndef DEVICES_SERIAL_H
2#define DEVICES_SERIAL_H
3
4#include <stdint.h>
5
6void serial_init_queue (void);
7void serial_putc (uint8_t);
8void serial_flush (void);
9void serial_notify (void);
10
11#endif /* devices/serial.h */
diff --git a/pintos-progos/devices/shutdown.c b/pintos-progos/devices/shutdown.c
new file mode 100644
index 0000000..7ff9a95
--- /dev/null
+++ b/pintos-progos/devices/shutdown.c
@@ -0,0 +1,131 @@
1#include "devices/shutdown.h"
2#include <console.h>
3#include <stdio.h>
4#include "devices/kbd.h"
5#include "devices/serial.h"
6#include "devices/timer.h"
7#include "threads/io.h"
8#include "threads/thread.h"
9#ifdef USERPROG
10#include "userprog/exception.h"
11#endif
12#ifdef FILESYS
13#include "devices/block.h"
14#include "filesys/filesys.h"
15#endif
16
17/* Keyboard control register port. */
18#define CONTROL_REG 0x64
19
20/* How to shut down when shutdown() is called. */
21static enum shutdown_type how = SHUTDOWN_NONE;
22
23static void print_stats (void);
24
25/* Shuts down the machine in the way configured by
26 shutdown_configure(). If the shutdown type is SHUTDOWN_NONE
27 (which is the default), returns without doing anything. */
28void
29shutdown (void)
30{
31 switch (how)
32 {
33 case SHUTDOWN_POWER_OFF:
34 shutdown_power_off ();
35 break;
36
37 case SHUTDOWN_REBOOT:
38 shutdown_reboot ();
39 break;
40
41 default:
42 /* Nothing to do. */
43 break;
44 }
45}
46
47/* Sets TYPE as the way that machine will shut down when Pintos
48 execution is complete. */
49void
50shutdown_configure (enum shutdown_type type)
51{
52 how = type;
53}
54
55/* Reboots the machine via the keyboard controller. */
56void
57shutdown_reboot (void)
58{
59 printf ("Rebooting...\n");
60
61 /* See [kbd] for details on how to program the keyboard
62 * controller. */
63 for (;;)
64 {
65 int i;
66
67 /* Poll keyboard controller's status byte until
68 * 'input buffer empty' is reported. */
69 for (i = 0; i < 0x10000; i++)
70 {
71 if ((inb (CONTROL_REG) & 0x02) == 0)
72 break;
73 timer_udelay (2);
74 }
75
76 timer_udelay (50);
77
78 /* Pulse bit 0 of the output port P2 of the keyboard controller.
79 * This will reset the CPU. */
80 outb (CONTROL_REG, 0xfe);
81 timer_udelay (50);
82 }
83}
84
85/* Powers down the machine we're running on,
86 as long as we're running on Bochs or QEMU. */
87void
88shutdown_power_off (void)
89{
90 const char s[] = "Shutdown";
91 const char *p;
92
93#ifdef FILESYS
94 filesys_done ();
95#endif
96
97 print_stats ();
98
99 printf ("Powering off...\n");
100 serial_flush ();
101
102 /* This is a special power-off sequence supported by Bochs and
103 QEMU, but not by physical hardware. */
104 for (p = s; *p != '\0'; p++)
105 outb (0x8900, *p);
106
107 /* This will power off a VMware VM if "gui.exitOnCLIHLT = TRUE"
108 is set in its configuration file. (The "pintos" script does
109 that automatically.) */
110 asm volatile ("cli; hlt" : : : "memory");
111
112 /* None of those worked. */
113 printf ("still running...\n");
114 for (;;);
115}
116
117/* Print statistics about Pintos execution. */
118static void
119print_stats (void)
120{
121 timer_print_stats ();
122 thread_print_stats ();
123#ifdef FILESYS
124 block_print_stats ();
125#endif
126 console_print_stats ();
127 kbd_print_stats ();
128#ifdef USERPROG
129 exception_print_stats ();
130#endif
131}
diff --git a/pintos-progos/devices/shutdown.h b/pintos-progos/devices/shutdown.h
new file mode 100644
index 0000000..dc4f942
--- /dev/null
+++ b/pintos-progos/devices/shutdown.h
@@ -0,0 +1,19 @@
1#ifndef DEVICES_SHUTDOWN_H
2#define DEVICES_SHUTDOWN_H
3
4#include <debug.h>
5
6/* How to shut down when Pintos has nothing left to do. */
7enum shutdown_type
8 {
9 SHUTDOWN_NONE, /* Loop forever. */
10 SHUTDOWN_POWER_OFF, /* Power off the machine (if possible). */
11 SHUTDOWN_REBOOT, /* Reboot the machine (if possible). */
12 };
13
14void shutdown (void);
15void shutdown_configure (enum shutdown_type);
16void shutdown_reboot (void) NO_RETURN;
17void shutdown_power_off (void) NO_RETURN;
18
19#endif /* devices/shutdown.h */
diff --git a/pintos-progos/devices/speaker.c b/pintos-progos/devices/speaker.c
new file mode 100644
index 0000000..5052005
--- /dev/null
+++ b/pintos-progos/devices/speaker.c
@@ -0,0 +1,68 @@
1#include "devices/speaker.h"
2#include "devices/pit.h"
3#include "threads/io.h"
4#include "threads/interrupt.h"
5#include "devices/timer.h"
6
7/* Speaker port enable I/O register. */
8#define SPEAKER_PORT_GATE 0x61
9
10/* Speaker port enable bits. */
11#define SPEAKER_GATE_ENABLE 0x03
12
13/* Sets the PC speaker to emit a tone at the given FREQUENCY, in
14 Hz. */
15void
16speaker_on (int frequency)
17{
18 if (frequency >= 20 && frequency <= 20000)
19 {
20 /* Set the timer channel that's connected to the speaker to
21 output a square wave at the given FREQUENCY, then
22 connect the timer channel output to the speaker. */
23 enum intr_level old_level = intr_disable ();
24 pit_configure_channel (2, 3, frequency);
25 outb (SPEAKER_PORT_GATE, inb (SPEAKER_PORT_GATE) | SPEAKER_GATE_ENABLE);
26 intr_set_level (old_level);
27 }
28 else
29 {
30 /* FREQUENCY is outside the range of normal human hearing.
31 Just turn off the speaker. */
32 speaker_off ();
33 }
34}
35
36/* Turn off the PC speaker, by disconnecting the timer channel's
37 output from the speaker. */
38void
39speaker_off (void)
40{
41 enum intr_level old_level = intr_disable ();
42 outb (SPEAKER_PORT_GATE, inb (SPEAKER_PORT_GATE) & ~SPEAKER_GATE_ENABLE);
43 intr_set_level (old_level);
44}
45
46/* Briefly beep the PC speaker. */
47void
48speaker_beep (void)
49{
50 /* Only attempt to beep the speaker if interrupts are enabled,
51 because we don't want to freeze the machine during the beep.
52 We could add a hook to the timer interrupt to avoid that
53 problem, but then we'd risk failing to ever stop the beep if
54 Pintos crashes for some unrelated reason. There's nothing
55 more annoying than a machine whose beeping you can't stop
56 without a power cycle.
57
58 We can't just enable interrupts while we sleep. For one
59 thing, we get called (indirectly) from printf, which should
60 always work, even during boot before we're ready to enable
61 interrupts. */
62 if (intr_get_level () == INTR_ON)
63 {
64 speaker_on (440);
65 timer_msleep (250);
66 speaker_off ();
67 }
68}
diff --git a/pintos-progos/devices/speaker.h b/pintos-progos/devices/speaker.h
new file mode 100644
index 0000000..98cef7b
--- /dev/null
+++ b/pintos-progos/devices/speaker.h
@@ -0,0 +1,8 @@
1#ifndef DEVICES_SPEAKER_H
2#define DEVICES_SPEAKER_H
3
4void speaker_on (int frequency);
5void speaker_off (void);
6void speaker_beep (void);
7
8#endif /* devices/speaker.h */
diff --git a/pintos-progos/devices/timer.c b/pintos-progos/devices/timer.c
new file mode 100644
index 0000000..befaaae
--- /dev/null
+++ b/pintos-progos/devices/timer.c
@@ -0,0 +1,246 @@
1#include "devices/timer.h"
2#include <debug.h>
3#include <inttypes.h>
4#include <round.h>
5#include <stdio.h>
6#include "devices/pit.h"
7#include "threads/interrupt.h"
8#include "threads/synch.h"
9#include "threads/thread.h"
10
11/* See [8254] for hardware details of the 8254 timer chip. */
12
13#if TIMER_FREQ < 19
14#error 8254 timer requires TIMER_FREQ >= 19
15#endif
16#if TIMER_FREQ > 1000
17#error TIMER_FREQ <= 1000 recommended
18#endif
19
20/* Number of timer ticks since OS booted. */
21static int64_t ticks;
22
23/* Number of loops per timer tick.
24 Initialized by timer_calibrate(). */
25static unsigned loops_per_tick;
26
27static intr_handler_func timer_interrupt;
28static bool too_many_loops (unsigned loops);
29static void busy_wait (int64_t loops);
30static void real_time_sleep (int64_t num, int32_t denom);
31static void real_time_delay (int64_t num, int32_t denom);
32
33/* Sets up the timer to interrupt TIMER_FREQ times per second,
34 and registers the corresponding interrupt. */
35void
36timer_init (void)
37{
38 pit_configure_channel (0, 2, TIMER_FREQ);
39 intr_register_ext (0x20, timer_interrupt, "8254 Timer");
40}
41
42/* Calibrates loops_per_tick, used to implement brief delays. */
43void
44timer_calibrate (void)
45{
46 unsigned high_bit, test_bit;
47
48 ASSERT (intr_get_level () == INTR_ON);
49 printf ("Calibrating timer... ");
50
51 /* Approximate loops_per_tick as the largest power-of-two
52 still less than one timer tick. */
53 loops_per_tick = 1u << 10;
54 while (!too_many_loops (loops_per_tick << 1))
55 {
56 loops_per_tick <<= 1;
57 ASSERT (loops_per_tick != 0);
58 }
59
60 /* Refine the next 8 bits of loops_per_tick. */
61 high_bit = loops_per_tick;
62 for (test_bit = high_bit >> 1; test_bit != high_bit >> 10; test_bit >>= 1)
63 if (!too_many_loops (high_bit | test_bit))
64 loops_per_tick |= test_bit;
65
66 printf ("%'"PRIu64" loops/s.\n", (uint64_t) loops_per_tick * TIMER_FREQ);
67}
68
69/* Returns the number of timer ticks since the OS booted. */
70int64_t
71timer_ticks (void)
72{
73 enum intr_level old_level = intr_disable ();
74 int64_t t = ticks;
75 intr_set_level (old_level);
76 return t;
77}
78
79/* Returns the number of timer ticks elapsed since THEN, which
80 should be a value once returned by timer_ticks(). */
81int64_t
82timer_elapsed (int64_t then)
83{
84 return timer_ticks () - then;
85}
86
87/* Sleeps for approximately TICKS timer ticks. Interrupts must
88 be turned on. */
89void
90timer_sleep (int64_t ticks)
91{
92 int64_t start = timer_ticks ();
93
94 ASSERT (intr_get_level () == INTR_ON);
95 while (timer_elapsed (start) < ticks)
96 thread_yield ();
97}
98
99/* Sleeps for approximately MS milliseconds. Interrupts must be
100 turned on. */
101void
102timer_msleep (int64_t ms)
103{
104 real_time_sleep (ms, 1000);
105}
106
107/* Sleeps for approximately US microseconds. Interrupts must be
108 turned on. */
109void
110timer_usleep (int64_t us)
111{
112 real_time_sleep (us, 1000 * 1000);
113}
114
115/* Sleeps for approximately NS nanoseconds. Interrupts must be
116 turned on. */
117void
118timer_nsleep (int64_t ns)
119{
120 real_time_sleep (ns, 1000 * 1000 * 1000);
121}
122
123/* Busy-waits for approximately MS milliseconds. Interrupts need
124 not be turned on.
125
126 Busy waiting wastes CPU cycles, and busy waiting with
127 interrupts off for the interval between timer ticks or longer
128 will cause timer ticks to be lost. Thus, use timer_msleep()
129 instead if interrupts are enabled. */
130void
131timer_mdelay (int64_t ms)
132{
133 real_time_delay (ms, 1000);
134}
135
136/* Sleeps for approximately US microseconds. Interrupts need not
137 be turned on.
138
139 Busy waiting wastes CPU cycles, and busy waiting with
140 interrupts off for the interval between timer ticks or longer
141 will cause timer ticks to be lost. Thus, use timer_usleep()
142 instead if interrupts are enabled. */
143void
144timer_udelay (int64_t us)
145{
146 real_time_delay (us, 1000 * 1000);
147}
148
149/* Sleeps execution for approximately NS nanoseconds. Interrupts
150 need not be turned on.
151
152 Busy waiting wastes CPU cycles, and busy waiting with
153 interrupts off for the interval between timer ticks or longer
154 will cause timer ticks to be lost. Thus, use timer_nsleep()
155 instead if interrupts are enabled.*/
156void
157timer_ndelay (int64_t ns)
158{
159 real_time_delay (ns, 1000 * 1000 * 1000);
160}
161
162/* Prints timer statistics. */
163void
164timer_print_stats (void)
165{
166 printf ("Timer: %"PRId64" ticks\n", timer_ticks ());
167}
168
169/* Timer interrupt handler. */
170static void
171timer_interrupt (struct intr_frame *args UNUSED)
172{
173 ticks++;
174 thread_tick ();
175}
176
177/* Returns true if LOOPS iterations waits for more than one timer
178 tick, otherwise false. */
179static bool
180too_many_loops (unsigned loops)
181{
182 /* Wait for a timer tick. */
183 int64_t start = ticks;
184 while (ticks == start)
185 barrier ();
186
187 /* Run LOOPS loops. */
188 start = ticks;
189 busy_wait (loops);
190
191 /* If the tick count changed, we iterated too long. */
192 barrier ();
193 return start != ticks;
194}
195
196/* Iterates through a simple loop LOOPS times, for implementing
197 brief delays.
198
199 Marked NO_INLINE because code alignment can significantly
200 affect timings, so that if this function was inlined
201 differently in different places the results would be difficult
202 to predict. */
203static void NO_INLINE
204busy_wait (int64_t loops)
205{
206 while (loops-- > 0)
207 barrier ();
208}
209
210/* Sleep for approximately NUM/DENOM seconds. */
211static void
212real_time_sleep (int64_t num, int32_t denom)
213{
214 /* Convert NUM/DENOM seconds into timer ticks, rounding down.
215
216 (NUM / DENOM) s
217 ---------------------- = NUM * TIMER_FREQ / DENOM ticks.
218 1 s / TIMER_FREQ ticks
219 */
220 int64_t ticks = num * TIMER_FREQ / denom;
221
222 ASSERT (intr_get_level () == INTR_ON);
223 if (ticks > 0)
224 {
225 /* We're waiting for at least one full timer tick. Use
226 timer_sleep() because it will yield the CPU to other
227 processes. */
228 timer_sleep (ticks);
229 }
230 else
231 {
232 /* Otherwise, use a busy-wait loop for more accurate
233 sub-tick timing. */
234 real_time_delay (num, denom);
235 }
236}
237
238/* Busy-wait for approximately NUM/DENOM seconds. */
239static void
240real_time_delay (int64_t num, int32_t denom)
241{
242 /* Scale the numerator and denominator down by 1000 to avoid
243 the possibility of overflow. */
244 ASSERT (denom % 1000 == 0);
245 busy_wait (loops_per_tick * num / 1000 * TIMER_FREQ / (denom / 1000));
246}
diff --git a/pintos-progos/devices/timer.h b/pintos-progos/devices/timer.h
new file mode 100644
index 0000000..cd3d6bb
--- /dev/null
+++ b/pintos-progos/devices/timer.h
@@ -0,0 +1,29 @@
1#ifndef DEVICES_TIMER_H
2#define DEVICES_TIMER_H
3
4#include <round.h>
5#include <stdint.h>
6
7/* Number of timer interrupts per second. */
8#define TIMER_FREQ 100
9
10void timer_init (void);
11void timer_calibrate (void);
12
13int64_t timer_ticks (void);
14int64_t timer_elapsed (int64_t);
15
16/* Sleep and yield the CPU to other threads. */
17void timer_sleep (int64_t ticks);
18void timer_msleep (int64_t milliseconds);
19void timer_usleep (int64_t microseconds);
20void timer_nsleep (int64_t nanoseconds);
21
22/* Busy waits. */
23void timer_mdelay (int64_t milliseconds);
24void timer_udelay (int64_t microseconds);
25void timer_ndelay (int64_t nanoseconds);
26
27void timer_print_stats (void);
28
29#endif /* devices/timer.h */
diff --git a/pintos-progos/devices/vga.c b/pintos-progos/devices/vga.c
new file mode 100644
index 0000000..f421b61
--- /dev/null
+++ b/pintos-progos/devices/vga.c
@@ -0,0 +1,172 @@
1#include "devices/vga.h"
2#include <round.h>
3#include <stdint.h>
4#include <stddef.h>
5#include <string.h>
6#include "devices/speaker.h"
7#include "threads/io.h"
8#include "threads/interrupt.h"
9#include "threads/vaddr.h"
10
11/* VGA text screen support. See [FREEVGA] for more information. */
12
13/* Number of columns and rows on the text display. */
14#define COL_CNT 80
15#define ROW_CNT 25
16
17/* Current cursor position. (0,0) is in the upper left corner of
18 the display. */
19static size_t cx, cy;
20
21/* Attribute value for gray text on a black background. */
22#define GRAY_ON_BLACK 0x07
23
24/* Framebuffer. See [FREEVGA] under "VGA Text Mode Operation".
25 The character at (x,y) is fb[y][x][0].
26 The attribute at (x,y) is fb[y][x][1]. */
27static uint8_t (*fb)[COL_CNT][2];
28
29static void clear_row (size_t y);
30static void cls (void);
31static void newline (void);
32static void move_cursor (void);
33static void find_cursor (size_t *x, size_t *y);
34
35/* Initializes the VGA text display. */
36static void
37init (void)
38{
39 /* Already initialized? */
40 static bool inited;
41 if (!inited)
42 {
43 fb = ptov (0xb8000);
44 find_cursor (&cx, &cy);
45 inited = true;
46 }
47}
48
49/* Writes C to the VGA text display, interpreting control
50 characters in the conventional ways. */
51void
52vga_putc (int c)
53{
54 /* Disable interrupts to lock out interrupt handlers
55 that might write to the console. */
56 enum intr_level old_level = intr_disable ();
57
58 init ();
59
60 switch (c)
61 {
62 case '\n':
63 newline ();
64 break;
65
66 case '\f':
67 cls ();
68 break;
69
70 case '\b':
71 if (cx > 0)
72 cx--;
73 break;
74
75 case '\r':
76 cx = 0;
77 break;
78
79 case '\t':
80 cx = ROUND_UP (cx + 1, 8);
81 if (cx >= COL_CNT)
82 newline ();
83 break;
84
85 case '\a':
86 intr_set_level (old_level);
87 speaker_beep ();
88 intr_disable ();
89 break;
90
91 default:
92 fb[cy][cx][0] = c;
93 fb[cy][cx][1] = GRAY_ON_BLACK;
94 if (++cx >= COL_CNT)
95 newline ();
96 break;
97 }
98
99 /* Update cursor position. */
100 move_cursor ();
101
102 intr_set_level (old_level);
103}
104
105/* Clears the screen and moves the cursor to the upper left. */
106static void
107cls (void)
108{
109 size_t y;
110
111 for (y = 0; y < ROW_CNT; y++)
112 clear_row (y);
113
114 cx = cy = 0;
115 move_cursor ();
116}
117
118/* Clears row Y to spaces. */
119static void
120clear_row (size_t y)
121{
122 size_t x;
123
124 for (x = 0; x < COL_CNT; x++)
125 {
126 fb[y][x][0] = ' ';
127 fb[y][x][1] = GRAY_ON_BLACK;
128 }
129}
130
131/* Advances the cursor to the first column in the next line on
132 the screen. If the cursor is already on the last line on the
133 screen, scrolls the screen upward one line. */
134static void
135newline (void)
136{
137 cx = 0;
138 cy++;
139 if (cy >= ROW_CNT)
140 {
141 cy = ROW_CNT - 1;
142 memmove (&fb[0], &fb[1], sizeof fb[0] * (ROW_CNT - 1));
143 clear_row (ROW_CNT - 1);
144 }
145}
146
147/* Moves the hardware cursor to (cx,cy). */
148static void
149move_cursor (void)
150{
151 /* See [FREEVGA] under "Manipulating the Text-mode Cursor". */
152 uint16_t cp = cx + COL_CNT * cy;
153 outw (0x3d4, 0x0e | (cp & 0xff00));
154 outw (0x3d4, 0x0f | (cp << 8));
155}
156
157/* Reads the current hardware cursor position into (*X,*Y). */
158static void
159find_cursor (size_t *x, size_t *y)
160{
161 /* See [FREEVGA] under "Manipulating the Text-mode Cursor". */
162 uint16_t cp;
163
164 outb (0x3d4, 0x0e);
165 cp = inb (0x3d5) << 8;
166
167 outb (0x3d4, 0x0f);
168 cp |= inb (0x3d5);
169
170 *x = cp % COL_CNT;
171 *y = cp / COL_CNT;
172}
diff --git a/pintos-progos/devices/vga.h b/pintos-progos/devices/vga.h
new file mode 100644
index 0000000..59690fb
--- /dev/null
+++ b/pintos-progos/devices/vga.h
@@ -0,0 +1,6 @@
1#ifndef DEVICES_VGA_H
2#define DEVICES_VGA_H
3
4void vga_putc (int);
5
6#endif /* devices/vga.h */