summaryrefslogtreecommitdiff
blob: b91d54f2f5edf67654dc5603a3e335caf2e2bf7e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
 * Extended boot option ROM support.
 *
 * Copyright IBM, Corp. 2007
 *
 * Authors:
 *  Anthony Liguori   <aliguori@us.ibm.com>
 *
 * This work is licensed under the terms of the GNU GPL, version 2.  See
 * the COPYING file in the top-level directory.
 *
 */

#include "hw.h"
#include "pc.h"
#include "isa.h"
#include "block.h"

/* Extended Boot ROM suport */

union extboot_cmd
{
    uint16_t type;
    struct {
	uint16_t type;
	uint16_t cylinders;
	uint16_t heads;
	uint16_t sectors;
	uint64_t nb_sectors;
    } query_geometry;
    struct {
	uint16_t type;
	uint16_t nb_sectors;
	uint16_t segment;
	uint16_t offset;
	uint64_t sector;
    } xfer;
};

static void get_translated_chs(BlockDriverState *bs, int *c, int *h, int *s)
{
    bdrv_get_geometry_hint(bs, c, h, s);

    if (*c <= 1024) {
	*c >>= 0;
	*h <<= 0;
    } else if (*c <= 2048) {
	*c >>= 1;
	*h <<= 1;
    } else if (*c <= 4096) {
	*c >>= 2;
	*h <<= 2;
    } else if (*c <= 8192) {
	*c >>= 3;
	*h <<= 3;
    } else {
	*c >>= 4;
	*h <<= 4;
    }

    /* what is the correct algorithm for this?? */
    if (*h == 256) {
	*h = 255;
	*c = *c + 1;
    }
}

static uint32_t extboot_read(void *opaque, uint32_t addr)
{
    int *pcmd = opaque;
    return *pcmd;
}

static void extboot_write_cmd(void *opaque, uint32_t addr, uint32_t value)
{
    union extboot_cmd cmd;
    BlockDriverState *bs = opaque;
    int cylinders, heads, sectors, err;
    uint64_t nb_sectors;
    target_phys_addr_t pa = 0;
    int blen = 0;
    void *buf = NULL;

    cpu_physical_memory_read((value & 0xFFFF) << 4, (uint8_t *)&cmd,
                             sizeof(cmd));

    if (cmd.type == 0x01 || cmd.type == 0x02) {
	pa = cmd.xfer.segment * 16 + cmd.xfer.offset;
        blen = cmd.xfer.nb_sectors * 512;
        buf = qemu_memalign(512, blen);
    }

    switch (cmd.type) {
    case 0x00:
        get_translated_chs(bs, &cylinders, &heads, &sectors);
	bdrv_get_geometry(bs, &nb_sectors);
	cmd.query_geometry.cylinders = cylinders;
	cmd.query_geometry.heads = heads;
	cmd.query_geometry.sectors = sectors;
	cmd.query_geometry.nb_sectors = nb_sectors;
	break;
    case 0x01:
	err = bdrv_read(bs, cmd.xfer.sector, buf, cmd.xfer.nb_sectors);
	if (err)
	    printf("Read failed\n");

        cpu_physical_memory_write(pa, buf, blen);

	break;
    case 0x02:
        cpu_physical_memory_read(pa, buf, blen);

	err = bdrv_write(bs, cmd.xfer.sector, buf, cmd.xfer.nb_sectors);
	if (err)
	    printf("Write failed\n");

	break;
    }

    cpu_physical_memory_write((value & 0xFFFF) << 4, (uint8_t *)&cmd,
                              sizeof(cmd));
    if (buf)
        qemu_free(buf);
}

void extboot_init(BlockDriverState *bs, int cmd)
{
    int *pcmd;

    pcmd = qemu_mallocz(sizeof(int));

    *pcmd = cmd;
    register_ioport_read(0x404, 1, 1, extboot_read, pcmd);
    register_ioport_write(0x405, 1, 2, extboot_write_cmd, bs);
}